Commit 28fd54b3 authored by Christophe Massiot's avatar Christophe Massiot

* dvb/sim.h, examples/dvb_ecmg.c, examples/dvb_ecmg_test.c: Implement a DVB Simulcrypt ECMG server.

parent 9313f054
/*****************************************************************************
* sim.h: ETSI TS 103 194 DVB Simulcrypt
*****************************************************************************
* Copyright (C) 2010 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*****************************************************************************/
/*
* Normative references:
* - ETSI TS 101 197 V1.2.1 (2002-02) (DVB Simulcrypt - part 1)
* - ETSI TR 102 035 V1.1.1 (2002-04) (Implementation guidelines of Simulcrypt)
* - ETSI TS 103 197 V1.5.1 (2008-10) (DVB Simulcrypt)
*/
#ifndef __BITSTREAM_DVB_SIM_H__
#define __BITSTREAM_DVB_SIM_H__
#ifdef __cplusplus
extern "C"
{
#endif
/*****************************************************************************
* Generic "Type, Length, Value" message
*****************************************************************************/
#define TLV_HEADER_SIZE 5
#define TLV_PARAM_SIZE 4
#define TLV_PARAM_EMPTY 0
#define TLV_PARAM_EMPTY_SIZE 2
typedef struct tlv_param_count_t {
uint16_t i_type;
uint16_t i_min;
uint16_t i_max;
} tlv_param_count_t;
static inline void tlv_append_empty(uint8_t *p_tlv)
{
/* prepare a dummy parameter for later appending */
p_tlv[TLV_HEADER_SIZE] = TLV_PARAM_EMPTY >> 8;
p_tlv[TLV_HEADER_SIZE + 1] = TLV_PARAM_EMPTY & 0xff;
}
static inline void tlv_set_version(uint8_t *p_tlv, uint8_t i_version)
{
p_tlv[0] = i_version;
}
static inline uint8_t tlv_get_version(uint8_t *p_tlv)
{
return p_tlv[0];
}
static inline void tlv_set_type(uint8_t *p_tlv, uint16_t i_type)
{
p_tlv[1] = i_type >> 8;
p_tlv[2] = i_type & 0xff;
}
static inline uint16_t tlv_get_type(uint8_t *p_tlv)
{
return (p_tlv[1] << 8) | p_tlv[2];
}
static inline void tlv_set_length(uint8_t *p_tlv, uint16_t i_length)
{
p_tlv[3] = i_length >> 8;
p_tlv[4] = i_length & 0xff;
}
static inline uint16_t tlv_get_length(uint8_t *p_tlv)
{
return (p_tlv[3] << 8) | p_tlv[4];
}
static inline void tlvn_set_type(uint8_t *p_tlv_n, uint16_t i_type)
{
p_tlv_n[0] = i_type >> 8;
p_tlv_n[1] = i_type & 0xff;
}
static inline uint16_t tlvn_get_type(uint8_t *p_tlv_n)
{
return (p_tlv_n[0] << 8) | p_tlv_n[1];
}
static inline void tlvn_set_length(uint8_t *p_tlv_n, uint16_t i_length)
{
p_tlv_n[2] = i_length >> 8;
p_tlv_n[3] = i_length & 0xff;
}
static inline uint16_t tlvn_get_length(uint8_t *p_tlv_n)
{
return (p_tlv_n[2] << 8) | p_tlv_n[3];
}
static inline void tlvn_append_empty(uint8_t *p_tlv_n)
{
uint16_t i_length = tlvn_get_length(p_tlv_n);
p_tlv_n[TLV_PARAM_SIZE + i_length] = TLV_PARAM_EMPTY >> 8;
p_tlv_n[TLV_PARAM_SIZE + i_length + 1] = TLV_PARAM_EMPTY & 0xff;
}
static inline uint8_t *tlv_get_param(uint8_t *p_tlv, uint16_t n)
{
uint16_t i_tlv_size = tlv_get_length(p_tlv);
uint8_t *p_tlv_n = p_tlv + TLV_HEADER_SIZE;
if (p_tlv_n - p_tlv - TLV_HEADER_SIZE > i_tlv_size) return NULL;
while (n) {
if (p_tlv_n + TLV_PARAM_SIZE - p_tlv - TLV_HEADER_SIZE > i_tlv_size)
return NULL;
p_tlv_n += TLV_PARAM_SIZE + tlvn_get_length(p_tlv_n);
n--;
}
if (p_tlv_n - p_tlv - TLV_HEADER_SIZE >= i_tlv_size) return NULL;
return p_tlv_n;
}
static inline bool tlv_validate_param(const uint8_t *p_tlv,
const uint8_t *p_tlv_n, uint16_t i_length)
{
uint16_t i_tlv_size = tlv_get_length(p_tlv);
return (p_tlv_n + TLV_PARAM_SIZE + i_length - p_tlv - TLV_HEADER_SIZE
<= i_tlv_size);
}
static inline bool tlv_validate(const uint8_t *p_tlv)
{
uint16_t i_tlv_size = tlv_get_length(p_tlv);
const uint8_t *p_tlv_n = p_tlv + TLV_HEADER_SIZE;
while (p_tlv_n + TLV_PARAM_SIZE - p_tlv - TLV_HEADER_SIZE <= i_tlv_size
&& p_tlv_n + TLV_PARAM_SIZE + tlvn_get_length(p_tlv_n)
- p_tlv - TLV_HEADER_SIZE <= i_tlv_size)
p_tlv_n += TLV_PARAM_SIZE + tlvn_get_length(p_tlv_n);
return (p_tlv_n - p_tlv - TLV_HEADER_SIZE == i_tlv_size);
}
static inline uint8_t *tlv_find_param(uint8_t *p_tlv, uint16_t i_type,
uint16_t n)
{
uint8_t *p_param;
uint8_t j = 0;
while ((p_param = tlv_get_param(p_tlv, j)) != NULL) {
j++;
if (tlvn_get_type(p_param) == i_type) {
if (!n) return p_param;
n--;
}
}
return NULL;
}
static inline uint16_t tlv_count_param(uint8_t *p_tlv, uint16_t i_type)
{
uint16_t i_count = 0;
uint8_t *p_tlv_n;
uint16_t j = 0;
while ((p_tlv_n = tlv_get_param(p_tlv, j)) != NULL) {
j++;
if (tlvn_get_type(p_tlv_n) == i_type)
i_count++;
}
return i_count;
}
static inline bool tlv_validate_count_param(uint8_t *p_tlv,
tlv_param_count_t *p_count)
{
uint16_t i_count = tlv_count_param(p_tlv, p_count->i_type);
return (i_count >= p_count->i_min) && (i_count <= p_count->i_max);
}
#define TLV_DECLARE_PARAM(intf, name, param, utype, type) \
static inline bool intf##_append_##name(uint8_t *p_tlv, type i_##name) \
{ \
uint8_t *p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0); \
int i; \
if (!tlv_validate_param(p_tlv, p_tlv_n, \
sizeof(type) + TLV_PARAM_EMPTY_SIZE)) \
return false; \
tlvn_set_type(p_tlv_n, param); \
tlvn_set_length(p_tlv_n, sizeof(type)); \
for (i = 0; i < sizeof(type); i++) \
p_tlv_n[4 + i] = ((utype)(i_##name) >> 8 * (sizeof(type) - i - 1)) \
& 0xff; \
tlvn_append_empty(p_tlv_n); \
return true; \
} \
\
static inline type intf##_find_##name(uint8_t *p_tlv, uint16_t n) \
{ \
const uint8_t *p_tlv_n = tlv_find_param(p_tlv, param, n); \
type i_##name = (type)(p_tlv_n[4]) << 8 * (sizeof(type) - 1); \
int i; \
for (i = 1; i < sizeof(type); i++) \
i_##name |= (utype)(p_tlv_n[4 + i]) << 8 * (sizeof(type) - i - 1); \
return i_##name; \
}
static inline bool tlv_append_data(uint8_t *p_tlv, uint16_t i_type,
const uint8_t *p_data, uint16_t i_length)
{
uint8_t *p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
int i;
if (!tlv_validate_param(p_tlv, p_tlv_n,
i_length + TLV_PARAM_EMPTY_SIZE))
return false;
tlvn_set_type(p_tlv_n, i_type);
tlvn_set_length(p_tlv_n, i_length);
memcpy(p_tlv_n + 4, p_data, i_length);
tlvn_append_empty(p_tlv_n);
return true;
}
static inline uint8_t *tlv_find_data(uint8_t *p_tlv, uint16_t i_type,
uint16_t n, uint16_t *pi_length)
{
const uint8_t *p_tlv_n = tlv_find_param(p_tlv, i_type, n);
*pi_length = tlvn_get_length(p_tlv_n);
return p_tlv_n + 4;
}
/*****************************************************************************
* ECMG
*****************************************************************************/
/* message types */
#define ECMG_TYPE_CHANNEL_SETUP 0x1
#define ECMG_TYPE_CHANNEL_TEST 0x2
#define ECMG_TYPE_CHANNEL_STATUS 0x3
#define ECMG_TYPE_CHANNEL_CLOSE 0x4
#define ECMG_TYPE_CHANNEL_ERROR 0x5
#define ECMG_TYPE_STREAM_SETUP 0x101
#define ECMG_TYPE_STREAM_TEST 0x102
#define ECMG_TYPE_STREAM_STATUS 0x103
#define ECMG_TYPE_STREAM_CLOSEREQ 0x104
#define ECMG_TYPE_STREAM_CLOSERESP 0x105
#define ECMG_TYPE_STREAM_ERROR 0x106
#define ECMG_TYPE_CW 0x201
#define ECMG_TYPE_ECM 0x202
/* parameter types */
#define ECMG_PARAM_SUPERCASID 0x1
#define ECMG_PARAM_SECTIONTSPKT 0x2
#define ECMG_PARAM_DELAYSTART 0x3
#define ECMG_PARAM_DELAYSTOP 0x4
#define ECMG_PARAM_TRANSDELAYSTART 0x5
#define ECMG_PARAM_TRANSDELAYSTOP 0x6
#define ECMG_PARAM_REPPERIOD 0x7
#define ECMG_PARAM_MAXSTREAMS 0x8
#define ECMG_PARAM_MINCPDUR 0x9
#define ECMG_PARAM_LEADCW 0xa
#define ECMG_PARAM_CWPERMSG 0xb
#define ECMG_PARAM_MAXCOMPTIME 0xc
#define ECMG_PARAM_ACCESSCRIT 0xd
#define ECMG_PARAM_CHANNELID 0xe
#define ECMG_PARAM_STREAMID 0xf
#define ECMG_PARAM_NOMCPDUR 0x10
#define ECMG_PARAM_ACCESSCRITMODE 0x11
#define ECMG_PARAM_CPNUMBER 0x12
#define ECMG_PARAM_CPDUR 0x13
#define ECMG_PARAM_CPCWCOMB 0x14
#define ECMG_PARAM_ECM 0x15
#define ECMG_PARAM_ACDELAYSTART 0x16
#define ECMG_PARAM_ACDELAYSTOP 0x17
#define ECMG_PARAM_CWENCRYPT 0x18
#define ECMG_PARAM_ECMID 0x19
#define ECMG_PARAM_ERRORSTATUS 0x7000
#define ECMG_PARAM_ERRORINFO 0x7001
static inline void ecmg_init(uint8_t *p_tlv)
{
tlv_append_empty(p_tlv);
}
TLV_DECLARE_PARAM(ecmg, supercasid, ECMG_PARAM_SUPERCASID, uint32_t, uint32_t)
TLV_DECLARE_PARAM(ecmg, sectiontspkt, ECMG_PARAM_SECTIONTSPKT, uint8_t, uint8_t)
TLV_DECLARE_PARAM(ecmg, delaystart, ECMG_PARAM_DELAYSTART, uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, delaystop, ECMG_PARAM_DELAYSTOP, uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, transdelaystart, ECMG_PARAM_TRANSDELAYSTART,
uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, transdelaystop, ECMG_PARAM_TRANSDELAYSTOP,
uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, repperiod, ECMG_PARAM_REPPERIOD, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, maxstreams, ECMG_PARAM_MAXSTREAMS, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, mincpdur, ECMG_PARAM_MINCPDUR, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, leadcw, ECMG_PARAM_LEADCW, uint8_t, uint8_t)
TLV_DECLARE_PARAM(ecmg, cwpermsg, ECMG_PARAM_CWPERMSG, uint8_t, uint8_t)
TLV_DECLARE_PARAM(ecmg, maxcomptime, ECMG_PARAM_MAXCOMPTIME, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, channelid, ECMG_PARAM_CHANNELID, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, streamid, ECMG_PARAM_STREAMID, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, nomcpdur, ECMG_PARAM_NOMCPDUR, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, accesscritmode, ECMG_PARAM_ACCESSCRITMODE,
uint8_t, bool)
TLV_DECLARE_PARAM(ecmg, cpnumber, ECMG_PARAM_CPNUMBER, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, cpdur, ECMG_PARAM_CPDUR, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, acdelaystart, ECMG_PARAM_ACDELAYSTART,
uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, acdelaystop, ECMG_PARAM_ACDELAYSTOP, uint16_t, int16_t)
TLV_DECLARE_PARAM(ecmg, ecmid, ECMG_PARAM_ECMID, uint16_t, uint16_t)
TLV_DECLARE_PARAM(ecmg, errorstatus, ECMG_PARAM_ERRORSTATUS, uint16_t, uint16_t)
static inline void ecmgcw_set_cpnum(uint8_t *p_param, uint16_t i_cpnum)
{
p_param[0] = i_cpnum >> 8;
p_param[1] = i_cpnum & 0xff;
}
static inline uint16_t ecmgcw_get_cpnum(uint8_t *p_param)
{
return (p_param[0] << 8) | p_param[1];
}
static inline uint8_t *ecmgcw_get_cw(uint8_t *p_param)
{
return p_param + 2;
}
static inline bool ecmg_validate_param(const uint8_t *p_tlv_n)
{
static const uint16_t pi_ecmg_params_minlength[] = {
/* 0x0 */ 0, 4, 1, 2, 2, 2, 2, 2,
/* 0x8 */ 2, 2, 1, 1, 2, 0, 2, 2,
/* 0x10 */ 2, 1, 2, 2, 0, 0, 2, 2,
/* 0x18 */ 0, 2
};
uint16_t i_type = tlvn_get_type(p_tlv_n);
uint16_t i_length = tlvn_get_length(p_tlv_n);
if (i_type <= ECMG_PARAM_ECMID) {
if (i_length < pi_ecmg_params_minlength[i_type]) return false;
} else if (i_type == ECMG_PARAM_ERRORSTATUS) {
if (i_length < 2) return false;
}
return true;
}
static inline bool ecmg_validate(uint8_t *p_tlv)
{
static const tlv_param_count_t p_ecmg_params_channel_setup[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_SUPERCASID, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_channel_test[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_channel_status[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_SECTIONTSPKT, 1, 1},
{ECMG_PARAM_ACDELAYSTART, 0, 1}, {ECMG_PARAM_ACDELAYSTOP, 0, 1},
{ECMG_PARAM_DELAYSTART, 1, 1}, {ECMG_PARAM_DELAYSTOP, 1, 1},
{ECMG_PARAM_TRANSDELAYSTART, 0, 1}, {ECMG_PARAM_TRANSDELAYSTOP, 0, 1},
{ECMG_PARAM_REPPERIOD, 1, 1}, {ECMG_PARAM_MAXSTREAMS, 1, 1},
{ECMG_PARAM_MINCPDUR, 1, 1}, {ECMG_PARAM_LEADCW, 1, 1},
{ECMG_PARAM_CWPERMSG, 1, 1}, {ECMG_PARAM_MAXCOMPTIME, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_channel_close[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_channel_error[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_ERRORSTATUS, 1, UINT16_MAX},
{0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_stream_setup[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1},
{ECMG_PARAM_ECMID, 0, 1}, {ECMG_PARAM_NOMCPDUR, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_stream_test[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_stream_status[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1},
{ECMG_PARAM_ECMID, 0, 1}, {ECMG_PARAM_ACCESSCRITMODE, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_stream_close[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_stream_error[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1},
{ECMG_PARAM_ERRORSTATUS, 1, UINT16_MAX}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_cw[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1},
{ECMG_PARAM_CPNUMBER, 1, 1}, {ECMG_PARAM_CWENCRYPT, 0, 1},
{ECMG_PARAM_CPCWCOMB, 1, UINT16_MAX}, {ECMG_PARAM_CPDUR, 0, 1},
{ECMG_PARAM_ACCESSCRIT, 0, 1}, {0, 0, 0}
};
static const tlv_param_count_t p_ecmg_params_ecm[] = {
{ECMG_PARAM_CHANNELID, 1, 1}, {ECMG_PARAM_STREAMID, 1, 1},
{ECMG_PARAM_CPNUMBER, 1, 1}, {ECMG_PARAM_ECM, 1, 1}, {0, 0, 0}
};
const tlv_param_count_t *p_param = {0, 0, 0};
uint8_t *p_tlv_n;
int j = 0;
switch (tlv_get_type(p_tlv)) {
case ECMG_TYPE_CHANNEL_SETUP:
p_param = p_ecmg_params_channel_setup; break;
case ECMG_TYPE_CHANNEL_TEST:
p_param = p_ecmg_params_channel_test; break;
case ECMG_TYPE_CHANNEL_STATUS:
p_param = p_ecmg_params_channel_status; break;
case ECMG_TYPE_CHANNEL_CLOSE:
p_param = p_ecmg_params_channel_close; break;
case ECMG_TYPE_CHANNEL_ERROR:
p_param = p_ecmg_params_channel_error; break;
case ECMG_TYPE_STREAM_SETUP:
p_param = p_ecmg_params_stream_setup; break;
case ECMG_TYPE_STREAM_TEST:
p_param = p_ecmg_params_stream_test; break;
case ECMG_TYPE_STREAM_STATUS:
p_param = p_ecmg_params_stream_status; break;
case ECMG_TYPE_STREAM_CLOSEREQ:
case ECMG_TYPE_STREAM_CLOSERESP:
p_param = p_ecmg_params_stream_close; break;
case ECMG_TYPE_STREAM_ERROR:
p_param = p_ecmg_params_stream_error; break;
case ECMG_TYPE_CW:
p_param = p_ecmg_params_cw; break;
case ECMG_TYPE_ECM:
p_param = p_ecmg_params_ecm; break;
default:
break;
}
while (p_param->i_type)
if (!tlv_validate_count_param(p_tlv, p_param++))
return false;
while ((p_tlv_n = tlv_get_param(p_tlv, j)) != NULL) {
j++;
if (!ecmg_validate_param(p_tlv_n)) return false;
}
return true;
}
#ifdef __cplusplus
}
#endif
#endif
# $Id$ # $Id$
CFLAGS = -Wall -O2 CFLAGS = -Wall -O2 -g
LDFLAGS = LDFLAGS =
OBJ = dvb_print_si OBJ = dvb_print_si dvb_ecmg dvb_ecmg_test
all: $(OBJ) all: $(OBJ)
......
/*****************************************************************************
* dvb_ecmg.c: Example of a basic DVB Simulcrypt ECMG server (ETSI TS 103 197)
*****************************************************************************
* Copyright (C) 2010 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*****************************************************************************/
/*
* You usually want to start this from xinetd, like:
*
* service dvb_ecmg
* {
* port = 6900
* disable = no
* type = UNLISTED
* id = dvb_ecmg
* socket_type = stream
* protocol = tcp
* user = nobody
* wait = no
* server = /usr/local/bin/dvb_ecmg.sh
* }
*
* With the following dvb_ecmg.sh wrapper:
*
* #!/bin/sh
* mkfifo /tmp/dvb_ecmg_stderr.$$
* logger -t "dvb_ecmg[$$]" < /tmp/dvb_ecmg_stderr.$$ &
* /usr/local/bin/dvb_ecmg 2>/tmp/dvb_ecmg_stderr.$$
* rm /tmp/dvb_ecmg_stderr.$$
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <bitstream/dvb/sim.h>
#include <bitstream/mpeg/psi.h>
/* normative size is 64 ko, but we will not output messages longer than this */
#define MAX_TLV_SIZE (1024 - TLV_HEADER_SIZE)
#define SELECT_TIMEOUT 10 /* s */
#define DELAY_START 200 /* ms */
#define DELAY_STOP 0
#define REP_PERIOD 300 /* ms */
#define MAX_STREAMS 500
#define MIN_CP_DUR 3 /* 100 ms */
#define LEAD_CW 1
#define CW_PER_MSG 2
#define MAX_COMP_TIME 100 /* ms */
/*****************************************************************************
* Local declarations
*****************************************************************************/
typedef struct stream_t {
uint16_t i_streamid;
uint16_t i_ecmid;
uint16_t i_cp_duration;
} stream_t;
static stream_t **pp_streams = NULL;
static int i_nb_streams = 0;
static bool b_init = true;
static uint8_t i_version = 1;
static uint16_t i_channelid = 0;
static uint32_t i_supercasid;
/*****************************************************************************
* read_wrapper
*****************************************************************************/
ssize_t read_wrapper(void *p_buf, size_t i_count)
{
size_t i_received = 0;
do {
ssize_t i_read = read(STDIN_FILENO, p_buf + i_received,
i_count - i_received);
if (i_read < 0 && errno == EINTR) continue;
if (i_read < 0) {
fprintf(stderr, "read error, aborting (%m)");
return i_read;
}
if (!i_read) {
fprintf(stderr, "read end-of-file, aborting");
return i_read;
}
i_received += i_read;
} while (i_received < i_count);
return i_count;
}
/*****************************************************************************
* write_wrapper
*****************************************************************************/
ssize_t write_wrapper(const void *p_buf, size_t i_count)
{
size_t i_sent = 0;
do {
ssize_t i_written = write(STDOUT_FILENO,
p_buf + i_sent, i_count - i_sent);
if (i_written < 0 && errno == EINTR) continue;
if (i_written < 0) {
fprintf(stderr, "write error (%m)");
return i_written;
}
i_sent += i_written;
} while (i_sent < i_count);
return i_count;
}
/*****************************************************************************
* send_channel_status
*****************************************************************************/
static void send_channel_status(void)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CHANNEL_STATUS);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_sectiontspkt(p_tlv, 0x0); /* sections */
ecmg_append_delaystart(p_tlv, DELAY_START);
ecmg_append_delaystop(p_tlv, DELAY_STOP);
ecmg_append_repperiod(p_tlv, REP_PERIOD);
ecmg_append_maxstreams(p_tlv, MAX_STREAMS);
ecmg_append_mincpdur(p_tlv, MIN_CP_DUR);
ecmg_append_leadcw(p_tlv, LEAD_CW);
ecmg_append_cwpermsg(p_tlv, CW_PER_MSG);
ecmg_append_maxcomptime(p_tlv, MAX_COMP_TIME);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_channel_test
*****************************************************************************/
static void send_channel_test(void)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
fprintf(stderr, "sending test on channel ID=0x%hx\n", i_channelid);
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CHANNEL_TEST);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_channel_error
*****************************************************************************/
static void send_channel_error(uint16_t i_wanted_channelid, uint16_t i_error)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
fprintf(stderr, "sending error on channel ID=0x%hx error=0x%hx\n",
i_wanted_channelid, i_error);
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CHANNEL_ERROR);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_wanted_channelid);
ecmg_append_errorstatus(p_tlv, i_error);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_stream_status
*****************************************************************************/
static void send_stream_status(stream_t *p_stream)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_STREAM_STATUS);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, p_stream->i_streamid);
if (i_version >= 2)
ecmg_append_ecmid(p_tlv, p_stream->i_ecmid);
ecmg_append_accesscritmode(p_tlv, true);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_stream_close
*****************************************************************************/
static void send_stream_close(stream_t *p_stream)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_STREAM_CLOSERESP);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, p_stream->i_streamid);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_stream_error
*****************************************************************************/
static void send_stream_error(uint16_t i_wanted_streamid, uint16_t i_error)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
fprintf(stderr, "sending error on stream ID=0x%hx error=0x%hx\n",
i_wanted_streamid, i_error);
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_STREAM_ERROR);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, i_wanted_streamid);
ecmg_append_errorstatus(p_tlv, i_error);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_ecm
*****************************************************************************/
static void send_ecm(stream_t *p_stream, uint16_t i_cp_number,
const uint8_t *p_ecm, uint16_t i_length)
{
uint8_t p_tlv[MAX_TLV_SIZE + i_length]; /* this is oversized */
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_ECM);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE + i_length);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, p_stream->i_streamid);
ecmg_append_cpnumber(p_tlv, i_cp_number);
tlv_append_data(p_tlv, ECMG_PARAM_ECM, p_ecm, i_length);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* build_ecm
*****************************************************************************/
static void build_ecm(stream_t *p_stream, uint16_t i_cp_number,
uint8_t * const ppi_cw[2],
uint8_t *p_accesscrit_param)
{
/* you will want to customize this function */
static const uint8_t pi_xor[8] =
{0x42, 0x12, 0x02, 0x24, 0x21, 0x20, 0x66, 0x88};
PSI_DECLARE(p_section);
uint8_t *pi_ecm;
int i, j;
psi_init(p_section, false);
psi_set_tableid(p_section, 0x80 | (i_cp_number & 0x1));
psi_set_length(p_section, 16);
pi_ecm = p_section + 3;
for (i = 0; i < 2; i++)
for (j = 0; j < 8; j++)
pi_ecm[i * 8 + j] = ppi_cw[i][j] ^ pi_xor[j];
send_ecm(p_stream, i_cp_number, p_section,
psi_get_length(p_section) + PSI_HEADER_SIZE);
}
/*****************************************************************************
* find_stream
*****************************************************************************/
static stream_t *find_stream(uint16_t i_streamid)
{
int i;
for (i = 0; i < i_nb_streams; i++)
if (pp_streams[i] != NULL && pp_streams[i]->i_streamid == i_streamid)
return pp_streams[i];
return NULL;
}
/*****************************************************************************
* handle_channel_setup
*****************************************************************************/
static void handle_channel_setup(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
if (!b_init) {
if (i_wanted_channelid != i_channelid)
send_channel_error(i_wanted_channelid, 0x6);
return;
}
i_version = tlv_get_version(p_tlv);
i_channelid = i_wanted_channelid;
i_supercasid = ecmg_find_supercasid(p_tlv, 0);
fprintf(stderr,
"starting channel ID=0x%hx version=%hhu super CAS ID=0x%x\n",
i_channelid, i_version, i_supercasid);
b_init = false;
send_channel_status();
}
/*****************************************************************************
* handle_channel_test
*****************************************************************************/
static void handle_channel_test(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
send_channel_status();
}
/*****************************************************************************
* handle_channel_close
*****************************************************************************/
static void handle_channel_close(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
int i;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
fprintf(stderr, "stopping channel ID=0x%x\n", i_channelid);
b_init = true;
for (i = 0; i < i_nb_streams; i++)
free(pp_streams[i]);
free(pp_streams);
pp_streams = NULL;
i_nb_streams = 0;
}
/*****************************************************************************
* handle_channel_error
*****************************************************************************/
static void handle_channel_error(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
fprintf(stderr, "receiving error channel ID=0x%hu error=0x%hx\n",
i_wanted_channelid, ecmg_find_errorstatus(p_tlv, 0));
}
/*****************************************************************************
* handle_stream_setup
*****************************************************************************/
static void handle_stream_setup(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
int i;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
if ((p_stream = find_stream(i_streamid)) != NULL) {
send_stream_error(i_streamid, 0x14);
return;
}
if (i_version >= 2 && tlv_count_param(p_tlv, ECMG_PARAM_ECMID) != 1) {
send_stream_error(i_streamid, 0x10);
return;
}
for (i = 0; i < i_nb_streams; i++) {
p_stream = pp_streams[i];
if (p_stream == NULL)
break;
}
if (i == i_nb_streams) {
p_stream = malloc(sizeof(stream_t));
pp_streams = realloc(pp_streams, ++i_nb_streams * sizeof(stream_t *));
pp_streams[i_nb_streams - 1] = p_stream;
}
p_stream->i_streamid = i_streamid;
if (i_version >= 2)
p_stream->i_ecmid = ecmg_find_ecmid(p_tlv, 0);
else
p_stream->i_ecmid = 0;
p_stream->i_cp_duration = ecmg_find_nomcpdur(p_tlv, 0);
fprintf(stderr,
"starting new stream id=0x%hx ecmid=0x%hx CP duration=%u ms\n",
p_stream->i_streamid, p_stream->i_ecmid,
p_stream->i_cp_duration * 100);
send_stream_status(p_stream);
}
/*****************************************************************************
* handle_stream_test
*****************************************************************************/
static void handle_stream_test(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
return;
}
send_stream_status(p_stream);
}
/*****************************************************************************
* handle_stream_close
*****************************************************************************/
static void handle_stream_close(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
int i;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
return;
}
fprintf(stderr, "stopping stream ID=0x%hx\n", i_streamid);
send_stream_close(p_stream);
for (i = 0; i < i_nb_streams; i++) {
if (pp_streams[i] == p_stream) {
pp_streams[i] = NULL;
break;
}
}
free(p_stream);
}
/*****************************************************************************
* handle_stream_error
*****************************************************************************/
static void handle_stream_error(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
fprintf(stderr,
"receiving error channel ID=0x%hu stream ID=0x%hx error=0x%hx\n",
i_wanted_channelid, i_streamid, ecmg_find_errorstatus(p_tlv, 0));
}
/*****************************************************************************
* handle_cw
*****************************************************************************/
static void handle_cw(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
uint16_t i_cp_number, i_cw_cp_number;
uint16_t i_cw_length;
uint8_t *p_cw_param, *p_accesscrit_param;
uint8_t *ppi_cw[2] = {NULL, NULL};
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
return;
}
if (tlv_count_param(p_tlv, ECMG_PARAM_CWENCRYPT)) {
send_stream_error(i_streamid, 0xe);
return;
}
if (tlv_count_param(p_tlv, ECMG_PARAM_CPCWCOMB) != 2) {
send_stream_error(i_streamid, 0xb);
return;
}
i_cp_number = ecmg_find_cpnumber(p_tlv, 0);
p_cw_param = tlv_find_data(p_tlv, ECMG_PARAM_CPCWCOMB, 0, &i_cw_length);
if (i_cw_length != 10) {
send_stream_error(i_streamid, 0xf);
return;
}
i_cw_cp_number = ecmgcw_get_cpnum(p_cw_param);
ppi_cw[i_cw_cp_number & 0x1] = ecmgcw_get_cw(p_cw_param);
p_cw_param = tlv_find_data(p_tlv, ECMG_PARAM_CPCWCOMB, 1, &i_cw_length);
if (i_cw_length != 10) {
send_stream_error(i_streamid, 0xf);
return;
}
i_cw_cp_number = ecmgcw_get_cpnum(p_cw_param);
ppi_cw[i_cw_cp_number & 0x1] = ecmgcw_get_cw(p_cw_param);
if (ppi_cw[0] == NULL || ppi_cw[1] == NULL) {
send_stream_error(i_streamid, 0xb);
return;
}
p_accesscrit_param = tlv_find_param(p_tlv, ECMG_PARAM_ACCESSCRIT, 0);
build_ecm(p_stream, i_cp_number, ppi_cw, p_accesscrit_param);
}
/*****************************************************************************
* Main loop
*****************************************************************************/
int main(int i_argc, char **ppsz_argv)
{
if (i_argc > 1) {
fprintf(stderr, "usage: %s < <input stream> > <output stream>\n",
ppsz_argv[0]);
return EXIT_FAILURE;
}
for ( ; ; ) {
uint8_t *p_tlv = malloc(TLV_HEADER_SIZE);
uint16_t i_type;
fd_set rset;
struct timeval timeout;
int i_ret;
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
timeout.tv_sec = SELECT_TIMEOUT;
timeout.tv_usec = 0;
if ((i_ret = select(STDIN_FILENO + 1, &rset, NULL, NULL, &timeout)) < 0)
return EXIT_FAILURE;
if (!i_ret) {
if (b_init) return EXIT_FAILURE;
/* timeout - send a packet so that the communication is reset
* if it is really dead */
send_channel_test();
continue;
}
if (read_wrapper(p_tlv, TLV_HEADER_SIZE) <= 0)
return EXIT_FAILURE;
p_tlv = realloc(p_tlv, TLV_HEADER_SIZE + tlv_get_length(p_tlv));
if (read_wrapper(p_tlv + TLV_HEADER_SIZE, tlv_get_length(p_tlv)) <= 0)
return EXIT_FAILURE;
i_type = tlv_get_type(p_tlv);
if (!tlv_validate(p_tlv)) {
send_channel_error(i_channelid, 0x1);
free(p_tlv);
continue;
}
if (!ecmg_validate(p_tlv)) {
send_channel_error(i_channelid, 0xf);
free(p_tlv);
continue;
}
if (b_init && i_type != ECMG_TYPE_CHANNEL_SETUP) {
send_channel_error(i_channelid, 0x6);
free(p_tlv);
continue;
}
if (tlv_get_version(p_tlv) > 3) {
send_channel_error(i_channelid, 0x2);
free(p_tlv);
continue;
}
switch (i_type) {
case ECMG_TYPE_CHANNEL_SETUP:
handle_channel_setup(p_tlv);
break;
case ECMG_TYPE_CHANNEL_TEST:
handle_channel_test(p_tlv);
break;
case ECMG_TYPE_CHANNEL_CLOSE:
handle_channel_close(p_tlv);
break;
case ECMG_TYPE_CHANNEL_ERROR:
handle_channel_error(p_tlv);
break;
case ECMG_TYPE_STREAM_SETUP:
handle_stream_setup(p_tlv);
break;
case ECMG_TYPE_STREAM_TEST:
handle_stream_test(p_tlv);
break;
case ECMG_TYPE_STREAM_CLOSEREQ:
handle_stream_close(p_tlv);
break;
case ECMG_TYPE_STREAM_ERROR:
handle_stream_error(p_tlv);
break;
case ECMG_TYPE_CHANNEL_STATUS:
case ECMG_TYPE_STREAM_STATUS:
break;
case ECMG_TYPE_CW:
handle_cw(p_tlv);
break;
default:
send_channel_error(i_channelid, 0x3);
break;
}
free(p_tlv);
}
return EXIT_SUCCESS;
}
/*****************************************************************************
* dvb_ecmg_test.c: Simulation of an SCS to stress-test an ECMG
*****************************************************************************
* Copyright (C) 2010 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*****************************************************************************/
/*
* You want to use this in conjunction with netcat, for instance:
*
* mkfifo /tmp/ecmg_in
* mkfifo /tmp/ecmg_out
* nc 192.168.0.1 6900 < /tmp/ecmg_out > /tmp/ecmg_in &
* dvb_ecmg_test 10 10 > /tmp/ecmg_out < /tmp/ecmg_in
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <time.h>
#include <bitstream/dvb/sim.h>
#include <bitstream/mpeg/psi.h>
/* normative size is 64 ko, but we will not output messages longer than this */
#define MAX_TLV_SIZE (1024 - TLV_HEADER_SIZE)
#define SELECT_TIMEOUT 1 /* s */
/*****************************************************************************
* Local declarations
*****************************************************************************/
typedef struct stream_t {
uint16_t i_streamid;
uint16_t i_ecmid;
uint16_t i_cp_number;
bool b_init;
} stream_t;
static stream_t **pp_streams = NULL;
static int i_nb_streams = 0;
static time_t i_last_time;
static bool b_init = true;
static uint8_t i_version = 1;
static uint16_t i_channelid = 0;
static uint32_t i_supercasid;
static uint8_t i_sectiontspkt;
static uint8_t i_nb_cw;
static uint8_t i_lead_cw;
static int i_wanted_streams;
static int i_period;
/*****************************************************************************
* read_wrapper
*****************************************************************************/
ssize_t read_wrapper(void *p_buf, size_t i_count)
{
size_t i_received = 0;
do {
ssize_t i_read = read(STDIN_FILENO, p_buf + i_received,
i_count - i_received);
if (i_read < 0 && errno == EINTR) continue;
if (i_read < 0) {
fprintf(stderr, "read error, aborting (%m)");
return i_read;
}
if (!i_read) {
fprintf(stderr, "read end-of-file, aborting (%m)");
return i_read;
}
i_received += i_read;
} while (i_received < i_count);
return i_count;
}
/*****************************************************************************
* write_wrapper
*****************************************************************************/
ssize_t write_wrapper(const void *p_buf, size_t i_count)
{
size_t i_sent = 0;
do {
ssize_t i_written = write(STDOUT_FILENO,
p_buf + i_sent, i_count - i_sent);
if (i_written < 0 && errno == EINTR) continue;
if (i_written < 0) {
fprintf(stderr, "write error (%m)");
return i_written;
}
i_sent += i_written;
} while (i_sent < i_count);
return i_count;
}
/*****************************************************************************
* send_channel_setup
*****************************************************************************/
static void send_channel_setup(void)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CHANNEL_SETUP);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_supercasid(p_tlv, i_supercasid);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_channel_error
*****************************************************************************/
static void send_channel_error(uint16_t i_wanted_channelid, uint16_t i_error)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
fprintf(stderr, "sending error on channel ID=0x%hx error=0x%hx\n",
i_wanted_channelid, i_error);
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CHANNEL_ERROR);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_wanted_channelid);
ecmg_append_errorstatus(p_tlv, i_error);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_stream_setup
*****************************************************************************/
static void send_stream_setup(stream_t *p_stream)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_STREAM_SETUP);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, p_stream->i_streamid);
if (i_version >= 2)
ecmg_append_ecmid(p_tlv, p_stream->i_ecmid);
ecmg_append_nomcpdur(p_tlv, i_period * 10);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_stream_error
*****************************************************************************/
static void send_stream_error(uint16_t i_wanted_streamid, uint16_t i_error)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
fprintf(stderr, "sending error on stream ID=0x%hx error=0x%hx\n",
i_wanted_streamid, i_error);
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_STREAM_ERROR);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, i_wanted_streamid);
ecmg_append_errorstatus(p_tlv, i_error);
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* send_cw
*****************************************************************************/
static void send_cw(stream_t *p_stream)
{
uint8_t p_tlv[MAX_TLV_SIZE];
uint8_t *p_tlv_n;
int i;
ecmg_init(p_tlv);
tlv_set_version(p_tlv, i_version);
tlv_set_type(p_tlv, ECMG_TYPE_CW);
/* length will be written at the end */
tlv_set_length(p_tlv, MAX_TLV_SIZE);
ecmg_append_channelid(p_tlv, i_channelid);
ecmg_append_streamid(p_tlv, p_stream->i_streamid);
ecmg_append_cpnumber(p_tlv, p_stream->i_cp_number);
for (i = 1; i <= i_nb_cw; i++) {
uint8_t p_cw[8 + 2];
uint8_t *pi_cw = ecmgcw_get_cw(p_cw);
uint32_t i_rand;
ecmgcw_set_cpnum(p_cw,
p_stream->i_cp_number + i - (i_nb_cw - i_lead_cw));
i_rand = rand();
memcpy(pi_cw, &i_rand, 4);
i_rand = rand();
memcpy(pi_cw + 4, &i_rand, 4);
tlv_append_data(p_tlv, ECMG_PARAM_CPCWCOMB, p_cw, 8 + 2);
}
p_tlv_n = tlv_find_param(p_tlv, TLV_PARAM_EMPTY, 0);
tlv_set_length(p_tlv, p_tlv_n - p_tlv - TLV_HEADER_SIZE);
write_wrapper(p_tlv, p_tlv_n - p_tlv);
}
/*****************************************************************************
* check_cw
*****************************************************************************/
static void check_cw(void)
{
int i_turn = i_last_time % i_period;
int i;
for (i = 0; i < i_nb_streams; i++)
if (!pp_streams[i]->b_init && (i % i_period) == i_turn)
send_cw(pp_streams[i]);
}
/*****************************************************************************
* find_stream
*****************************************************************************/
static stream_t *find_stream(uint16_t i_streamid)
{
int i;
for (i = 0; i < i_nb_streams; i++)
if (pp_streams[i] != NULL && pp_streams[i]->i_streamid == i_streamid)
return pp_streams[i];
return NULL;
}
/*****************************************************************************
* create_stream
*****************************************************************************/
static void create_stream(void)
{
stream_t *p_stream = malloc(sizeof(stream_t));
pp_streams = realloc(pp_streams, ++i_nb_streams * sizeof(stream_t *));
pp_streams[i_nb_streams - 1] = p_stream;
p_stream->i_streamid = i_nb_streams;
p_stream->i_ecmid = i_nb_streams;
p_stream->i_cp_number = UINT8_MAX;
p_stream->b_init = true;
send_stream_setup(p_stream);
fprintf(stderr, "starting new stream id=0x%hx\n", p_stream->i_streamid);
}
/*****************************************************************************
* handle_channel_status
*****************************************************************************/
static void handle_channel_status(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
exit(EXIT_FAILURE);
}
if (!b_init)
return;
if (ecmg_find_maxstreams(p_tlv, 0) < i_wanted_streams) {
fprintf(stderr, "not enough supported streams (%hu)\n",
ecmg_find_maxstreams(p_tlv, 0));
exit(EXIT_FAILURE);
}
if (ecmg_find_mincpdur(p_tlv, 0) > i_period * 10) {
fprintf(stderr, "crypto-period too short (%hu)\n",
ecmg_find_mincpdur(p_tlv, 0));
exit(EXIT_FAILURE);
}
i_sectiontspkt = ecmg_find_sectiontspkt(p_tlv, 0);
i_nb_cw = ecmg_find_cwpermsg(p_tlv, 0);
i_lead_cw = ecmg_find_leadcw(p_tlv, 0);
fprintf(stderr,
"starting channel ID=0x%hx version=%hhu super CAS ID=0x%x\n",
i_channelid, i_version, i_supercasid);
b_init = false;
}
/*****************************************************************************
* handle_channel_test
*****************************************************************************/
static void handle_channel_test(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
exit(EXIT_FAILURE);
}
fprintf(stderr, "channel test received\n");
exit(EXIT_FAILURE);
}
/*****************************************************************************
* handle_channel_error
*****************************************************************************/
static void handle_channel_error(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
fprintf(stderr, "receiving error channel ID=0x%hu error=0x%hx\n",
i_wanted_channelid, ecmg_find_errorstatus(p_tlv, 0));
exit(EXIT_FAILURE);
}
/*****************************************************************************
* handle_stream_status
*****************************************************************************/
static void handle_stream_status(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
exit(EXIT_FAILURE);
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
exit(EXIT_FAILURE);
}
if (i_version >= 2 && tlv_count_param(p_tlv, ECMG_PARAM_ECMID) != 1) {
send_stream_error(i_streamid, 0x10);
exit(EXIT_FAILURE);
}
p_stream->b_init = false;
}
/*****************************************************************************
* handle_stream_test
*****************************************************************************/
static void handle_stream_test(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
exit(EXIT_FAILURE);
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
exit(EXIT_FAILURE);
}
fprintf(stderr, "stream test received\n");
exit(EXIT_FAILURE);
}
/*****************************************************************************
* handle_stream_error
*****************************************************************************/
static void handle_stream_error(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
fprintf(stderr,
"receiving error channel ID=0x%hu stream ID=0x%hx error=0x%hx\n",
i_wanted_channelid, i_streamid, ecmg_find_errorstatus(p_tlv, 0));
}
/*****************************************************************************
* handle_ecm
*****************************************************************************/
static void handle_ecm(uint8_t *p_tlv)
{
uint16_t i_wanted_channelid = ecmg_find_channelid(p_tlv, 0);
uint16_t i_streamid = ecmg_find_streamid(p_tlv, 0);
stream_t *p_stream;
uint16_t i_cp_number;
uint8_t *p_ecm;
uint16_t i_ecm_length;
if (i_wanted_channelid != i_channelid) {
send_channel_error(i_wanted_channelid, 0x6);
return;
}
if ((p_stream = find_stream(i_streamid)) == NULL) {
send_stream_error(i_streamid, 0x7);
return;
}
i_cp_number = ecmg_find_cpnumber(p_tlv, 0);
if (i_cp_number != p_stream->i_cp_number)
fprintf(stderr, "late ECM packet\n");
if (i_sectiontspkt != 0) return;
p_ecm = tlv_find_data(p_tlv, ECMG_PARAM_ECM, 0, &i_ecm_length);
if (i_ecm_length < PSI_HEADER_SIZE) {
fprintf(stderr, "too short ECM (%hu)\n", i_ecm_length);
return;
}
if (i_ecm_length != psi_get_length(p_ecm) + PSI_HEADER_SIZE) {
fprintf(stderr, "ECM size mismatch\n");
return;
}
if (!psi_validate(p_ecm))
fprintf(stderr, "invalid PSI section\n");
}
/*****************************************************************************
* Main loop
*****************************************************************************/
int main(int i_argc, char **ppsz_argv)
{
if (i_argc != 3) {
fprintf(stderr, "usage: %s <# streams> <crypto-period (s)> < < <input stream> > <output stream>\n",
ppsz_argv[0]);
return EXIT_FAILURE;
}
i_wanted_streams = atoi(ppsz_argv[1]);
i_period = atoi(ppsz_argv[2]);
i_last_time = time(NULL);
srand(time(NULL));
i_channelid = rand();
i_supercasid = rand();
send_channel_setup();
for ( ; ; ) {
uint8_t *p_tlv = malloc(TLV_HEADER_SIZE);
uint16_t i_type;
fd_set rset;
struct timeval timeout;
int i_ret;
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
timeout.tv_sec = SELECT_TIMEOUT;
timeout.tv_usec = 0;
if ((i_ret = select(STDIN_FILENO + 1, &rset, NULL, NULL, &timeout)) < 0)
return EXIT_FAILURE;
if (!i_ret) {
goto no_packet;
}
if (read_wrapper(p_tlv, TLV_HEADER_SIZE) <= 0)
return EXIT_FAILURE;
p_tlv = realloc(p_tlv, TLV_HEADER_SIZE + tlv_get_length(p_tlv));
if (read_wrapper(p_tlv + TLV_HEADER_SIZE, tlv_get_length(p_tlv)) <= 0)
return EXIT_FAILURE;
i_type = tlv_get_type(p_tlv);
if (!tlv_validate(p_tlv)) {
send_channel_error(i_channelid, 0x1);
free(p_tlv);
exit(EXIT_FAILURE);
}
if (!ecmg_validate(p_tlv)) {
send_channel_error(i_channelid, 0xf);
free(p_tlv);
exit(EXIT_FAILURE);
}
if (b_init && i_type != ECMG_TYPE_CHANNEL_STATUS) {
send_channel_error(i_channelid, 0x6);
free(p_tlv);
exit(EXIT_FAILURE);
}
if (tlv_get_version(p_tlv) > 3) {
send_channel_error(i_channelid, 0x2);
free(p_tlv);
exit(EXIT_FAILURE);
}
switch (i_type) {
case ECMG_TYPE_CHANNEL_STATUS:
handle_channel_status(p_tlv);
break;
case ECMG_TYPE_CHANNEL_TEST:
handle_channel_test(p_tlv);
break;
case ECMG_TYPE_CHANNEL_ERROR:
handle_channel_error(p_tlv);
break;
case ECMG_TYPE_STREAM_STATUS:
handle_stream_status(p_tlv);
break;
case ECMG_TYPE_STREAM_TEST:
handle_stream_test(p_tlv);
break;
case ECMG_TYPE_STREAM_ERROR:
handle_stream_error(p_tlv);
break;
case ECMG_TYPE_ECM:
handle_ecm(p_tlv);
break;
default:
send_channel_error(i_channelid, 0x3);
break;
}
free(p_tlv);
no_packet:
if (!b_init) {
time_t i_time = time(NULL);
if (i_nb_streams < i_wanted_streams)
create_stream();
while (i_last_time < i_time) {
i_last_time++;
check_cw();
}
}
}
return EXIT_SUCCESS;
}
...@@ -469,7 +469,7 @@ int main(int i_argc, char **ppsz_argv) ...@@ -469,7 +469,7 @@ int main(int i_argc, char **ppsz_argv)
(!strcmp(ppsz_argv[1], "-h") || !strcmp(ppsz_argv[1], "--help"))) { (!strcmp(ppsz_argv[1], "-h") || !strcmp(ppsz_argv[1], "--help"))) {
fprintf(stderr, "usage: %s < <input file> [> <output>]\n", fprintf(stderr, "usage: %s < <input file> [> <output>]\n",
ppsz_argv[0]); ppsz_argv[0]);
return 1; return EXIT_FAILURE;
} }
memset(p_pids, 0, sizeof(p_pids)); memset(p_pids, 0, sizeof(p_pids));
...@@ -499,5 +499,5 @@ int main(int i_argc, char **ppsz_argv) ...@@ -499,5 +499,5 @@ int main(int i_argc, char **ppsz_argv)
} }
} }
return 0; return EXIT_SUCCESS;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment