[PR-175] capmt: add support for on-demand sockets per service id
This commit fixes simultaneous channel decryption in capmt oscam mode. Initial channel PMT data and PMT updates are sent to oscam via different capmt socket connections (a socket is created per channel SID). OScam is stopping decrypting when the socket is closed. More details in this thread: http://www.streamboard.tv/wbb2/thread.php?threadid=33323 Thanks to @posixx for days of testing :)
This commit is contained in:
parent
3de3fe191a
commit
7943c8b684
1 changed files with 138 additions and 36 deletions
174
src/capmt.c
174
src/capmt.c
|
@ -77,6 +77,7 @@
|
|||
#define INFO_SIZE (2+KEY_SIZE+KEY_SIZE)
|
||||
#define EVEN_OFF (2)
|
||||
#define ODD_OFF (2+KEY_SIZE)
|
||||
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
|
||||
static unsigned char ca_info[MAX_CA][MAX_INDEX][INFO_SIZE];
|
||||
|
||||
/**
|
||||
|
@ -189,7 +190,8 @@ typedef struct capmt {
|
|||
int capmt_oscam;
|
||||
|
||||
/* capmt sockets */
|
||||
int capmt_sock;
|
||||
int sids[MAX_SOCKETS];
|
||||
int capmt_sock[MAX_SOCKETS];
|
||||
int capmt_sock_ca0[MAX_CA];
|
||||
|
||||
/* thread flags */
|
||||
|
@ -206,43 +208,128 @@ typedef struct capmt {
|
|||
*
|
||||
*/
|
||||
static int
|
||||
capmt_send_msg(capmt_t *capmt, const uint8_t *buf, size_t len)
|
||||
capmt_send_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len)
|
||||
{
|
||||
return write(capmt->capmt_sock, buf, len);
|
||||
if (capmt->capmt_oscam) {
|
||||
int i, sent = 0;
|
||||
|
||||
// dumping current SID table
|
||||
for (i = 0; i < MAX_SOCKETS; i++)
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: SOCKETS TABLE DUMP [%d]: sid=%d socket=%d", __FUNCTION__, i, capmt->sids[i], capmt->capmt_sock[i]);
|
||||
if (sid == 0) {
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: got empty SID - returning from function", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// searching for the SID and socket
|
||||
int found = 0;
|
||||
for (i = 0; i < MAX_SOCKETS; i++) {
|
||||
if (capmt->sids[i] == sid) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: found sid, reusing socket, i=%d", __FUNCTION__, i);
|
||||
else { //not found - adding to first free in table
|
||||
for (i = 0; i < MAX_SOCKETS; i++) {
|
||||
if (capmt->sids[i] == 0) {
|
||||
capmt->sids[i] = sid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == MAX_SOCKETS) {
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: no free space for new SID!!!", __FUNCTION__);
|
||||
return -1;
|
||||
} else {
|
||||
capmt->sids[i] = sid;
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: added: i=%d", __FUNCTION__, i);
|
||||
}
|
||||
|
||||
// opening socket and sending
|
||||
if (capmt->capmt_sock[i] == 0) {
|
||||
capmt->capmt_sock[i] = tvh_socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
|
||||
struct sockaddr_un serv_addr_un;
|
||||
memset(&serv_addr_un, 0, sizeof(serv_addr_un));
|
||||
serv_addr_un.sun_family = AF_LOCAL;
|
||||
snprintf(serv_addr_un.sun_path, sizeof(serv_addr_un.sun_path), "%s", capmt->capmt_sockfile);
|
||||
|
||||
if (connect(capmt->capmt_sock[i], (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) != 0) {
|
||||
tvhlog(LOG_ERR, "capmt", "Cannot connect to %s, Do you have OSCam running?", capmt->capmt_sockfile);
|
||||
capmt->capmt_sock[i] = 0;
|
||||
} else
|
||||
tvhlog(LOG_DEBUG, "capmt", "created socket with socket_fd=%d", capmt->capmt_sock[i]);
|
||||
}
|
||||
if (capmt->capmt_sock[i] > 0) {
|
||||
sent = write(capmt->capmt_sock[i], buf, len);
|
||||
tvhlog(LOG_DEBUG, "capmt", "socket_fd=%d len=%d sent=%d", capmt->capmt_sock[i], (int)len, sent);
|
||||
if (sent != len) {
|
||||
tvhlog(LOG_ERR, "capmt", "%s: len != sent", __FUNCTION__);
|
||||
close(capmt->capmt_sock[i]);
|
||||
capmt->capmt_sock[i] = 0;
|
||||
}
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
else // standard old capmt mode
|
||||
return write(capmt->capmt_sock[0], buf, len);
|
||||
}
|
||||
|
||||
static void
|
||||
capmt_send_stop(capmt_service_t *t)
|
||||
{
|
||||
/* buffer for capmt */
|
||||
int pos = 0;
|
||||
uint8_t buf[4094];
|
||||
if (t->ct_capmt->capmt_oscam) {
|
||||
int i;
|
||||
// searching for socket to close
|
||||
for (i = 0; i < MAX_SOCKETS; i++)
|
||||
if (t->ct_capmt->sids[i] == t->ct_service->s_dvb_service_id)
|
||||
break;
|
||||
|
||||
capmt_header_t head = {
|
||||
.capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 },
|
||||
.capmt_list_management = CAPMT_LIST_ONLY,
|
||||
.program_number = t->ct_service->s_dvb_service_id,
|
||||
.version_number = 0,
|
||||
.current_next_indicator = 0,
|
||||
.program_info_length = 0,
|
||||
.capmt_cmd_id = CAPMT_CMD_NOT_SELECTED,
|
||||
};
|
||||
memcpy(&buf[pos], &head, sizeof(head));
|
||||
pos += sizeof(head);
|
||||
if (i == MAX_SOCKETS) {
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: socket to close not found", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t end[] = {
|
||||
0x01, (t->ct_seq >> 8) & 0xFF, t->ct_seq & 0xFF, 0x00, 0x06 };
|
||||
memcpy(&buf[pos], end, sizeof(end));
|
||||
pos += sizeof(end);
|
||||
buf[4] = ((pos - 6) >> 8);
|
||||
buf[5] = ((pos - 6) & 0xFF);
|
||||
buf[7] = t->ct_service->s_dvb_service_id >> 8;
|
||||
buf[8] = t->ct_service->s_dvb_service_id & 0xFF;
|
||||
buf[9] = 1;
|
||||
buf[10] = ((pos - 5 - 12) & 0xF00) >> 8;
|
||||
buf[11] = ((pos - 5 - 12) & 0xFF);
|
||||
// closing socket (oscam handle this as event and stop decrypting)
|
||||
tvhlog(LOG_DEBUG, "capmt", "%s: closing socket i=%d, socket_fd=%d", __FUNCTION__, i, t->ct_capmt->capmt_sock[i]);
|
||||
t->ct_capmt->sids[i] = 0;
|
||||
if (t->ct_capmt->capmt_sock[i] > 0)
|
||||
close(t->ct_capmt->capmt_sock[i]);
|
||||
t->ct_capmt->capmt_sock[i] = 0;
|
||||
} else { // standard old capmt mode
|
||||
/* buffer for capmt */
|
||||
int pos = 0;
|
||||
uint8_t buf[4094];
|
||||
|
||||
capmt_header_t head = {
|
||||
.capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 },
|
||||
.capmt_list_management = CAPMT_LIST_ONLY,
|
||||
.program_number = t->ct_service->s_dvb_service_id,
|
||||
.version_number = 0,
|
||||
.current_next_indicator = 0,
|
||||
.program_info_length = 0,
|
||||
.capmt_cmd_id = CAPMT_CMD_NOT_SELECTED,
|
||||
};
|
||||
memcpy(&buf[pos], &head, sizeof(head));
|
||||
pos += sizeof(head);
|
||||
|
||||
uint8_t end[] = {
|
||||
0x01, (t->ct_seq >> 8) & 0xFF, t->ct_seq & 0xFF, 0x00, 0x06 };
|
||||
memcpy(&buf[pos], end, sizeof(end));
|
||||
pos += sizeof(end);
|
||||
buf[4] = ((pos - 6) >> 8);
|
||||
buf[5] = ((pos - 6) & 0xFF);
|
||||
buf[7] = t->ct_service->s_dvb_service_id >> 8;
|
||||
buf[8] = t->ct_service->s_dvb_service_id & 0xFF;
|
||||
buf[9] = 1;
|
||||
buf[10] = ((pos - 5 - 12) & 0xF00) >> 8;
|
||||
buf[11] = ((pos - 5 - 12) & 0xFF);
|
||||
|
||||
capmt_send_msg(t->ct_capmt, buf, pos);
|
||||
capmt_send_msg(t->ct_capmt, t->ct_service->s_dvb_service_id, buf, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -457,9 +544,12 @@ capmt_thread(void *aux)
|
|||
th_dvb_adapter_t *tda;
|
||||
|
||||
while (capmt->capmt_running) {
|
||||
capmt->capmt_sock = -1;
|
||||
for (i = 0; i < MAX_CA; i++)
|
||||
capmt->capmt_sock_ca0[i] = -1;
|
||||
for (i = 0; i < MAX_SOCKETS; i++) {
|
||||
capmt->sids[i] = 0;
|
||||
capmt->capmt_sock[i] = 0;
|
||||
}
|
||||
capmt->capmt_connected = 0;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
@ -470,14 +560,14 @@ capmt_thread(void *aux)
|
|||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
/* open connection to camd.socket */
|
||||
capmt->capmt_sock = tvh_socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
capmt->capmt_sock[0] = tvh_socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
|
||||
struct sockaddr_un serv_addr_un;
|
||||
memset(&serv_addr_un, 0, sizeof(serv_addr_un));
|
||||
serv_addr_un.sun_family = AF_LOCAL;
|
||||
snprintf(serv_addr_un.sun_path, sizeof(serv_addr_un.sun_path), "%s", capmt->capmt_sockfile);
|
||||
|
||||
if (connect(capmt->capmt_sock, (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) == 0) {
|
||||
if (connect(capmt->capmt_sock[0], (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) == 0) {
|
||||
capmt->capmt_connected = 1;
|
||||
|
||||
/* open connection to emulated ca0 device */
|
||||
|
@ -503,8 +593,9 @@ capmt_thread(void *aux)
|
|||
capmt->capmt_connected = 0;
|
||||
|
||||
/* close opened sockets */
|
||||
if (capmt->capmt_sock > 0)
|
||||
close(capmt->capmt_sock);
|
||||
for (i = 0; i < MAX_SOCKETS; i++)
|
||||
if (capmt->capmt_sock[i] > 0)
|
||||
close(capmt->capmt_sock[i]);
|
||||
for (i = 0; i < MAX_CA; i++)
|
||||
if (capmt->capmt_sock_ca0[i] > 0)
|
||||
close(capmt->capmt_sock_ca0[i]);
|
||||
|
@ -540,6 +631,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t,
|
|||
capmt_t *capmt = ct->ct_capmt;
|
||||
int adapter_num = t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num;
|
||||
int total_caids = 0, current_caid = 0;
|
||||
int i;
|
||||
|
||||
caid_t *c;
|
||||
|
||||
|
@ -587,7 +679,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t,
|
|||
if ((cce->cce_ecmsize == len) && !memcmp(cce->cce_ecm, data, len))
|
||||
break; /* key already sent */
|
||||
|
||||
if(capmt->capmt_sock == -1) {
|
||||
if(!capmt->capmt_oscam && capmt->capmt_sock[0] == 0) {
|
||||
/* New key, but we are not connected (anymore), can not descramble */
|
||||
ct->ct_keystate = CT_UNKNOWN;
|
||||
break;
|
||||
|
@ -718,7 +810,17 @@ capmt_table_input(struct th_descrambler *td, struct service *t,
|
|||
buf[9] = pmtversion;
|
||||
pmtversion = (pmtversion + 1) & 0x1F;
|
||||
|
||||
capmt_send_msg(capmt, buf, pos);
|
||||
int found = 0;
|
||||
if (capmt->capmt_oscam) {
|
||||
for (i = 0; i < MAX_SOCKETS; i++) {
|
||||
if (capmt->sids[i] == sid) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((capmt->capmt_oscam && !found) || !capmt->capmt_oscam)
|
||||
capmt_send_msg(capmt, sid, buf, pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
Loading…
Add table
Reference in a new issue