tsdebug: add possibility to save the whole input mux with decrambler keys

This commit is contained in:
Jaroslav Kysela 2014-12-05 22:42:43 +01:00
parent b87eb898c8
commit 2b2cb3236e
15 changed files with 401 additions and 5 deletions

View file

@ -329,6 +329,10 @@ SRCS-${CONFIG_CAPMT} += \
SRCS-${CONFIG_CONSTCW} += \
src/descrambler/constcw.c
# TSDEBUGCW
SRCS-${CONFIG_TSDEBUG} += \
src/descrambler/tsdebugcw.c
# FFdecsa
ifneq ($(CONFIG_DVBCSA),yes)
FFDECSA-$(CONFIG_CAPMT) = yes

8
configure vendored
View file

@ -44,6 +44,7 @@ OPTIONS=(
"kqueue:no"
"dbus_1:auto"
"android:no"
"tsdebug:no"
)
#
@ -472,6 +473,13 @@ if enabled_or_auto dbus_1; then
fi
fi
#
# TSDebug
#
if enabled_or_auto tsdebug; then
enable mpegts_dvb
fi
# ###########################################################################
# Write config

View file

@ -296,6 +296,9 @@ caclient_start ( struct service *t )
if (cac->cac_enabled)
cac->cac_start(cac, t);
pthread_mutex_unlock(&caclients_mutex);
#if ENABLE_TSDEBUG
tsdebugcw_service_start(t);
#endif
}
void
@ -344,6 +347,9 @@ caclient_init(void)
pthread_mutex_init(&caclients_mutex, NULL);
TAILQ_INIT(&caclients);
#if ENABLE_TSDEBUG
tsdebugcw_init();
#endif
if (!(c = hts_settings_load("caclient")))
return;

View file

@ -75,8 +75,14 @@ const char *caclient_get_status(caclient_t *cac);
void caclient_init(void);
void caclient_done(void);
void tsdebugcw_service_start(struct service *t);
void tsdebugcw_new_keys(struct service *t, int type, uint8_t *odd, uint8_t *even);
void tsdebugcw_go(void);
void tsdebugcw_init(void);
caclient_t *cwc_create(void);
caclient_t *capmt_create(void);
caclient_t *constcw_create(void);
caclient_t *tsdebugcw_create(void);
#endif /* __TVH_CACLIENT_H__ */

View file

@ -252,6 +252,40 @@ descrambler_keys ( th_descrambler_t *td, int type,
fin:
pthread_mutex_unlock(&t->s_stream_mutex);
#if ENABLE_TSDEBUG
if (j) {
tsdebug_packet_t *tp = malloc(sizeof(*tp));
uint16_t keylen = dr->dr_csa.csa_keylen;
uint16_t sid = ((mpegts_service_t *)td->td_service)->s_dvb_service_id;
uint32_t pos = 0, crc;
mpegts_mux_t *mm = ((mpegts_service_t *)td->td_service)->s_dvb_mux;
if (!mm->mm_active)
return;
pthread_mutex_lock(&mm->mm_active->mmi_input->mi_output_lock);
tp->pos = mm->mm_tsdebug_pos;
memset(tp->pkt, 0xff, sizeof(tp->pkt));
tp->pkt[pos++] = 0x47; /* sync byte */
tp->pkt[pos++] = 0x1f; /* PID MSB */
tp->pkt[pos++] = 0xff; /* PID LSB */
tp->pkt[pos++] = 0x00; /* CC */
memcpy(tp->pkt + pos, "TVHeadendDescramblerKeys", 24);
pos += 24;
tp->pkt[pos++] = type & 0xff;
tp->pkt[pos++] = keylen & 0xff;
tp->pkt[pos++] = (sid >> 8) & 0xff;
tp->pkt[pos++] = sid & 0xff;
memcpy(tp->pkt + pos, even, keylen);
memcpy(tp->pkt + pos + keylen, odd, keylen);
pos += 2 * keylen;
crc = tvh_crc32(tp->pkt, pos, 0x859aa5ba);
tp->pkt[pos++] = (crc >> 24) & 0xff;
tp->pkt[pos++] = (crc >> 16) & 0xff;
tp->pkt[pos++] = (crc >> 8) & 0xff;
tp->pkt[pos++] = crc & 0xff;
TAILQ_INSERT_HEAD(&mm->mm_tsdebug_packets, tp, link);
pthread_mutex_unlock(&mm->mm_active->mmi_input->mi_output_lock);
}
#endif
}
static void

169
src/descrambler/tsdebugcw.c Normal file
View file

@ -0,0 +1,169 @@
/*
* tvheadend, tsdebug code word interface
* Copyright (C) 2014 Jaroslav Kysela
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include "tvheadend.h"
#include "caclient.h"
#include "service.h"
#include "input.h"
/**
*
*/
typedef struct tsdebugcw_service {
th_descrambler_t;
int tdcw_type;
uint8_t tdcw_key_even[16]; /* DES or AES key */
uint8_t tdcw_key_odd [16]; /* DES or AES key */
} tsdebugcw_service_t;
typedef struct tsdebugcw_request {
TAILQ_ENTRY(tsdebugcw_request) link;
tsdebugcw_service_t *ct;
} tsdebugcw_request_t;
pthread_mutex_t tsdebugcw_mutex;
TAILQ_HEAD(,tsdebugcw_request) tsdebugcw_requests;
/*
*
*/
static int
tsdebugcw_ecm_reset(th_descrambler_t *th)
{
return 1;
}
/**
* s_stream_mutex is held
*/
static void
tsdebugcw_service_destroy(th_descrambler_t *td)
{
tsdebugcw_service_t *ct = (tsdebugcw_service_t *)td;
tsdebugcw_request_t *ctr, *ctrnext;
pthread_mutex_lock(&tsdebugcw_mutex);
for (ctr = TAILQ_FIRST(&tsdebugcw_requests); ctr; ctr = ctrnext) {
ctrnext = TAILQ_NEXT(ctr, link);
if (ctr->ct == ct) {
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
free(ctr);
}
}
pthread_mutex_unlock(&tsdebugcw_mutex);
LIST_REMOVE(td, td_service_link);
free(ct->td_nicename);
free(ct);
}
/**
* global_lock is held. Not that we care about that, but either way, it is.
*/
void
tsdebugcw_service_start(service_t *t)
{
tsdebugcw_service_t *ct;
th_descrambler_t *td;
char buf[128];
extern const idclass_t mpegts_service_class;
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (td)
return;
ct = calloc(1, sizeof(tsdebugcw_service_t));
td = (th_descrambler_t *)ct;
snprintf(buf, sizeof(buf), "tsdebugcw");
td->td_nicename = strdup(buf);
td->td_service = t;
td->td_stop = tsdebugcw_service_destroy;
td->td_ecm_reset = tsdebugcw_ecm_reset;
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
}
/*
*
*/
void
tsdebugcw_new_keys(service_t *t, int type, uint8_t *odd, uint8_t *even)
{
static char empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
th_descrambler_t *td;
tsdebugcw_service_t *ct;
tsdebugcw_request_t *ctr;
int keylen = type == DESCRAMBLER_AES ? 16 : 8;
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
if (td->td_stop == tsdebugcw_service_destroy)
break;
if (!td)
return;
ct = (tsdebugcw_service_t *)td;
ct->tdcw_type = type;
if (memcmp(empty, odd, keylen))
memcpy(ct->tdcw_key_odd, odd, keylen);
if (memcmp(empty, even, keylen))
memcpy(ct->tdcw_key_even, even, keylen);
ctr = malloc(sizeof(*ctr));
ctr->ct = ct;
pthread_mutex_lock(&tsdebugcw_mutex);
TAILQ_INSERT_TAIL(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
}
/*
*
*/
void
tsdebugcw_go(void)
{
tsdebugcw_request_t *ctr;
tsdebugcw_service_t *ct;
while (1) {
pthread_mutex_lock(&tsdebugcw_mutex);
ctr = TAILQ_FIRST(&tsdebugcw_requests);
if (ctr)
TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
pthread_mutex_unlock(&tsdebugcw_mutex);
if (!ctr) break;
ct = ctr->ct;
descrambler_keys((th_descrambler_t *)ct, ct->tdcw_type,
ct->tdcw_key_odd, ct->tdcw_key_even);
free(ctr);
}
}
/*
*
*/
void
tsdebugcw_init(void)
{
pthread_mutex_init(&tsdebugcw_mutex, NULL);
TAILQ_INIT(&tsdebugcw_requests);
}

View file

@ -329,6 +329,12 @@ enum mpegts_mux_epg_flag
};
#define MM_EPG_LAST MM_EPG_ONLY_OPENTV_SKY_AUSAT
typedef struct tsdebug_packet {
TAILQ_ENTRY(tsdebug_packet) link;
uint8_t pkt[188];
off_t pos;
} tsdebug_packet_t;
/* Multiplex */
struct mpegts_mux
{
@ -421,6 +427,16 @@ struct mpegts_mux
int mm_epg;
char *mm_charset;
int mm_pmt_06_ac3;
/*
* TSDEBUG
*/
#if ENABLE_TSDEBUG
int mm_tsdebug_fd;
int mm_tsdebug_fd2;
off_t mm_tsdebug_pos;
TAILQ_HEAD(, tsdebug_packet) mm_tsdebug_packets;
#endif
};
/* Service */
@ -803,6 +819,28 @@ mpegts_pid_t * mpegts_input_open_pid
void mpegts_input_close_pid
( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
static inline void
tsdebug_write(mpegts_mux_t *mm, uint8_t *buf, size_t len)
{
#if ENABLE_TSDEBUG
ssize_t r = write(mm->mm_tsdebug_fd2, buf, len);
if (r != len && mm->mm_tsdebug_fd2 >= 0)
tvherror("tsdebug", "unable to write input data (%i)", errno);
#endif
}
static inline ssize_t
sbuf_tsdebug_read(mpegts_mux_t *mm, sbuf_t *sb, int fd)
{
#if ENABLE_TSDEBUG
ssize_t r = sbuf_read(sb, fd);
tsdebug_write(mm, sb->sb_data + sb->sb_ptr - r, r);
return r;
#else
return sbuf_read(sb, fd);
#endif
}
void mpegts_table_dispatch
(const uint8_t *sec, size_t r, void *mt);
static inline void mpegts_table_grab

View file

@ -47,6 +47,7 @@ iptv_http_data
pthread_mutex_lock(&iptv_lock);
tsdebug_write((mpegts_mux_t *)im, buf, len);
sbuf_append(&im->mm_iptv_buffer, buf, len);
if (len > 0)

View file

@ -137,6 +137,7 @@ iptv_rtp_read ( iptv_mux_t *im )
/* Move data */
len -= hlen;
tsdebug_write((mpegts_mux_t *)im, rtp + hlen, len);
sbuf_append(&im->mm_iptv_buffer, rtp + hlen, len);
res += len;
}

View file

@ -856,7 +856,7 @@ linuxdvb_frontend_input_thread ( void *aux )
if (ev[0].data.fd != dvr) break;
/* Read */
if ((n = sbuf_read(&sb, dvr)) < 0) {
if ((n = sbuf_tsdebug_read(mmi->mmi_mux, &sb, dvr)) < 0) {
if (ERRNO_AGAIN(errno))
continue;
if (errno == EOVERFLOW) {

View file

@ -29,6 +29,9 @@
#include <pthread.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/stat.h>
static void
mpegts_input_del_network ( mpegts_network_link_t *mnl );
@ -475,6 +478,34 @@ static void
mpegts_input_started_mux
( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
#if ENABLE_TSDEBUG
extern char *tvheadend_tsdebug;
static const char *tmpdir = "/tmp/tvheadend.tsdebug/";
char buf[128];
char path[PATH_MAX];
struct stat st;
if (!tvheadend_tsdebug && !stat(tmpdir, &st) && (st.st_mode & S_IFDIR) != 0)
tvheadend_tsdebug = (char *)tmpdir;
if (tvheadend_tsdebug && !strcmp(tvheadend_tsdebug, tmpdir) && stat(tmpdir, &st))
tvheadend_tsdebug = NULL;
if (tvheadend_tsdebug) {
mpegts_mux_nice_name(mmi->mmi_mux, buf, sizeof(buf));
snprintf(path, sizeof(path), "%s/%s-%li-%p-mux.ts", tvheadend_tsdebug,
buf, (long)dispatch_clock, mi);
mmi->mmi_mux->mm_tsdebug_fd = tvh_open(path, O_WRONLY | O_CREAT, 0600);
if (mmi->mmi_mux->mm_tsdebug_fd < 0)
tvherror("tsdebug", "unable to create file '%s' (%i)", path, errno);
snprintf(path, sizeof(path), "%s/%s-%li-%p-input.ts", tvheadend_tsdebug,
buf, (long)dispatch_clock, mi);
mmi->mmi_mux->mm_tsdebug_fd2 = tvh_open(path, O_WRONLY | O_CREAT, 0600);
if (mmi->mmi_mux->mm_tsdebug_fd2 < 0)
tvherror("tsdebug", "unable to create file '%s' (%i)", path, errno);
} else {
mmi->mmi_mux->mm_tsdebug_fd = -1;
mmi->mmi_mux->mm_tsdebug_fd2 = -1;
}
#endif
/* Deliver first TS packets as fast as possible */
mi->mi_last_dispatch = 0;
/* Wait for first TS packet */
@ -532,6 +563,19 @@ mpegts_input_stopped_mux
}
notify_reload("input_status");
mpegts_input_dbus_notify(mi, 0);
#if ENABLE_TSDEBUG
tsdebug_packet_t *tp;
close(mmi->mmi_mux->mm_tsdebug_fd);
close(mmi->mmi_mux->mm_tsdebug_fd2);
mmi->mmi_mux->mm_tsdebug_fd = -1;
mmi->mmi_mux->mm_tsdebug_fd2 = -1;
mmi->mmi_mux->mm_tsdebug_pos = 0;
while ((tp = TAILQ_FIRST(&mmi->mmi_mux->mm_tsdebug_packets)) != NULL) {
TAILQ_REMOVE(&mmi->mmi_mux->mm_tsdebug_packets, tp, link);
free(tp);
}
#endif
}
static int
@ -727,6 +771,37 @@ mpegts_input_table_waiting ( mpegts_input_t *mi, mpegts_mux_t *mm )
pthread_mutex_unlock(&mm->mm_tables_lock);
}
#if ENABLE_TSDEBUG
static void
tsdebug_check_tspkt( mpegts_mux_t *mm, uint8_t *pkt )
{
void tsdebugcw_new_keys(service_t *t, int type, uint8_t *odd, uint8_t *even);
uint32_t pos, type, keylen, sid, crc;
mpegts_service_t *t;
if (memcmp(pkt + 4, "TVHeadendDescramblerKeys", 24))
return;
pos = 4 + 24;
type = pkt[pos + 0];
keylen = pkt[pos + 1];
sid = (pkt[pos + 2] << 8) | pkt[pos + 3];
pos += 4 + 2 * keylen;
if (pos > 184)
return;
crc = (pkt[pos + 0] << 24) | (pkt[pos + 1] << 16) |
(pkt[pos + 2] << 8) | pkt[pos + 3];
if (crc != tvh_crc32(pkt, pos, 0x859aa5ba))
return;
LIST_FOREACH(t, &mm->mm_services, s_dvb_mux_link)
if (t->s_dvb_service_id == sid) break;
if (!t)
return;
pos = 4 + 24 + 4;
tvhdebug("descrambler", "Keys from MPEG-TS source (PID 0x1FFF)!");
tsdebugcw_new_keys((service_t *)t, type, pkt + pos, pkt + pos + keylen);
}
#endif
static void
mpegts_input_process
( mpegts_input_t *mi, mpegts_packet_t *mpkt )
@ -744,6 +819,9 @@ mpegts_input_process
mpegts_mux_t *mm = mpkt->mp_mux;
mpegts_mux_instance_t *mmi;
mpegts_pid_t *last_mp = NULL;
#if ENABLE_TSDEBUG
off_t tsdebug_pos = mm->mm_tsdebug_pos;
#endif
if (mm == NULL || (mmi = mm->mm_active) == NULL)
return;
@ -767,7 +845,12 @@ mpegts_input_process
pid &= 0x1FFF;
/* Ignore NUL packets */
if (pid == 0x1FFF) goto done;
if (pid == 0x1FFF) {
#if ENABLE_TSDEBUG
tsdebug_check_tspkt(mm, tsb);
#endif
goto done;
}
/* Remove in future or move it outside this loop */
lock_assert(&mi->mi_output_lock);
@ -841,11 +924,15 @@ mpegts_input_process
done:
tsb += 188;
#if ENABLE_TSDEBUG
mm->mm_tsdebug_pos += 188;
#endif
}
/* Raw stream */
if (tsb != mpkt->mp_data &&
LIST_FIRST(&mmi->mmi_streaming_pad.sp_targets) != NULL) {
streaming_message_t sm;
pktbuf_t *pb = pktbuf_alloc(mpkt->mp_data, tsb - mpkt->mp_data);
memset(&sm, 0, sizeof(sm));
@ -854,6 +941,27 @@ done:
streaming_pad_deliver(&mmi->mmi_streaming_pad, streaming_msg_clone(&sm));
pktbuf_ref_dec(pb);
}
#if ENABLE_TSDEBUG
{
tsdebug_packet_t *tp, *tp_next;
off_t pos = 0;
size_t used = tsb - mpkt->mp_data;
for (tp = TAILQ_FIRST(&mm->mm_tsdebug_packets); tp; tp = tp_next) {
tp_next = TAILQ_NEXT(tp, link);
assert((tp->pos % 188) == 0);
assert(tp->pos >= tsdebug_pos && tp->pos < tsdebug_pos + used);
if (mm->mm_tsdebug_fd >= 0) {
tvh_write(mm->mm_tsdebug_fd, mpkt->mp_data + pos, tp->pos - tsdebug_pos - pos);
tvh_write(mm->mm_tsdebug_fd, tp->pkt, 188);
}
pos = tp->pos - tsdebug_pos;
TAILQ_REMOVE(&mm->mm_tsdebug_packets, tp, link);
free(tp);
}
if (pos < used && mm->mm_tsdebug_fd >= 0)
tvh_write(mm->mm_tsdebug_fd, mpkt->mp_data + pos, used - pos);
}
#endif
/* Wake table */
if (table_wakeup)
@ -888,6 +996,14 @@ mpegts_input_thread ( void * p )
/* Cleanup */
free(mp);
#if ENABLE_TSDEBUG
{
extern void tsdebugcw_go(void);
tsdebugcw_go();
}
#endif
pthread_mutex_lock(&mi->mi_input_lock);
}

View file

@ -1228,6 +1228,7 @@ fast_exit:
(uint32_t)((uint16_t)nseq-(uint16_t)(seq+1));
seq = nseq;
/* Process */
tsdebug_write((mpegts_mux_t *)lm, p + pos, c - pos);
sbuf_append(&sb, p + pos, c - pos);
}
mpegts_input_recv_packets((mpegts_input_t*)lfe, mmi,

View file

@ -214,8 +214,8 @@ tsfile_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *t )
/* Check file is accessible */
if (lstat(mmi->mmi_tsfile_path, &st)) {
tvhlog(LOG_ERR, "tsfile", "mmi %p could not stat %s",
mmi, mmi->mmi_tsfile_path);
tvhlog(LOG_ERR, "tsfile", "mmi %p could not stat '%s' (%i)",
mmi, mmi->mmi_tsfile_path, errno);
mmi->mmi_tune_failed = 1;
return SM_CODE_TUNING_FAILED;
}

View file

@ -188,7 +188,7 @@ tvhdhomerun_frontend_input_thread ( void *aux )
if (nfds < 1) continue;
if (ev[0].data.fd != sockfd) break;
if((r = sbuf_read(&sb, sockfd)) < 0) {
if((r = sbuf_tsdebug_read(mmi->mmi_mux, &sb, sockfd)) < 0) {
/* whoopsy */
if(ERRNO_AGAIN(errno))
continue;

View file

@ -132,6 +132,7 @@ int tvheadend_webui_port;
int tvheadend_webui_debug;
int tvheadend_htsp_port;
int tvheadend_htsp_port_extra;
const char *tvheadend_tsdebug;
const char *tvheadend_cwd;
const char *tvheadend_webroot;
const tvh_caps_t tvheadend_capabilities[] = {
@ -469,7 +470,9 @@ main(int argc, char **argv)
opt_fileline = 0,
opt_threadid = 0,
opt_ipv6 = 0,
#if ENABLE_TSFILE
opt_tsfile_tuner = 0,
#endif
opt_dump = 0,
opt_xspf = 0,
opt_dbus = 0,
@ -560,9 +563,16 @@ main(int argc, char **argv)
OPT_STR, &opt_subscribe },
#if ENABLE_TSFILE || ENABLE_TSDEBUG
{ 0, NULL, "TODO: testing", OPT_BOOL, NULL },
#if ENABLE_TSFILE
{ 0, "tsfile_tuners", "Number of tsfile tuners", OPT_INT, &opt_tsfile_tuner },
{ 0, "tsfile", "tsfile input (mux file)", OPT_STR_LIST, &opt_tsfile },
#endif
#if ENABLE_TSDEBUG
{ 0, "tsdebug", "Output directory for tsdebug", OPT_STR, &tvheadend_tsdebug },
#endif
#endif
};
@ -957,7 +967,9 @@ main(int argc, char **argv)
if(opt_fork)
unlink(opt_pidpath);
#if ENABLE_TSFILE
free(opt_tsfile.str);
#endif
free(opt_satip_xml.str);
/* OpenSSL - welcome to the "cleanup" hell */