diff --git a/src/descrambler/capmt.c b/src/descrambler/capmt.c index fa38530c..a6702e45 100644 --- a/src/descrambler/capmt.c +++ b/src/descrambler/capmt.c @@ -67,6 +67,8 @@ typedef struct dmx_filter { uint8_t mode[DMX_FILTER_SIZE]; } dmx_filter_t; +#define DVBAPI_PROTOCOL_VERSION 1 + #define CA_SET_DESCR 0x40106f86 #define CA_SET_DESCR_X 0x866f1040 #define CA_SET_DESCR_AES 0x40106f87 @@ -77,6 +79,10 @@ typedef struct dmx_filter { #define DMX_STOP_X 0x2a6f0000 #define DMX_SET_FILTER 0x403c6f2b #define DMX_SET_FILTER_X 0x2b6f3c40 +#define DVBAPI_FILTER_DATA 0xFFFF0000 +#define DVBAPI_CLIENT_INFO 0xFFFF0001 +#define DVBAPI_SERVER_INFO 0xFFFF0002 + // ca_pmt_list_management values: #define CAPMT_LIST_MORE 0x00 // append a 'MORE' CAPMT object the list and start receiving the next object @@ -102,18 +108,20 @@ typedef struct dmx_filter { #define CAPMT_MSG_CLEAR 0x02 // limits -#define MAX_CA 16 -#define MAX_INDEX 64 -#define MAX_FILTER 64 -#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux -#define MAX_PIDS 64 // max opened pids +#define MAX_CA 16 +#define MAX_INDEX 64 +#define MAX_FILTER 64 +#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux +#define MAX_PIDS 64 // max opened pids +#define MAX_INFO_LEN 255 typedef enum { CAPMT_OSCAM_SO_WRAPPER, CAPMT_OSCAM_OLD, CAPMT_OSCAM_MULTILIST, CAPMT_OSCAM_TCP, - CAPMT_OSCAM_UNIX_SOCKET + CAPMT_OSCAM_UNIX_SOCKET, + CAPMT_OSCAM_NET_PROTO } capmt_oscam_mode_t; /** @@ -280,6 +288,7 @@ static void capmt_notify_server(capmt_t *capmt, capmt_service_t *ct, int force); static void capmt_send_request(capmt_service_t *ct, int lm); static void capmt_table_input(void *opaque, int pid, const uint8_t *data, int len); +static void capmt_send_client_info(capmt_t *capmt); /** * @@ -440,7 +449,7 @@ capmt_connect(capmt_t *capmt, int i) if (!capmt->capmt_running) return -1; - if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) { + if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) { char errbuf[256]; @@ -483,6 +492,8 @@ capmt_connect(capmt_t *capmt, int i) tvhlog(LOG_DEBUG, "capmt", "%s: Created socket %d", capmt_name(capmt), fd); capmt->capmt_sock[i] = fd; capmt->capmt_sock_reconnect[i]++; + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) + capmt_send_client_info(capmt); capmt_poll_add(capmt, fd, i + 1); } @@ -776,6 +787,19 @@ capmt_service_destroy(th_descrambler_t *td) free(ct); } +static void +capmt_send_client_info(capmt_t *capmt) +{ + char buf[MAX_INFO_LEN + 7]; + + *(uint32_t *)(buf + 0) = htonl(DVBAPI_CLIENT_INFO); + *(uint16_t *)(buf + 4) = htons(DVBAPI_PROTOCOL_VERSION); //supported protocol version + int len = snprintf(buf + 7, sizeof(buf) - 7, "Tvheadend %s", tvheadend_version); + buf[6] = len; + + capmt_write_msg(capmt, 0, 0, (uint8_t *)&buf, len + 7); +} + static void capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index, uint8_t filter_index, const uint8_t *data, int len, @@ -783,8 +807,7 @@ capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index, { uint8_t *buf = alloca(len + 6); - buf[0] = buf[1] = 0xff; - buf[2] = buf[3] = 0; + *(uint32_t *)(buf + 0) = htonl(DVBAPI_FILTER_DATA); buf[4] = demux_index; buf[5] = filter_index; memcpy(buf + 6, data, len); @@ -821,6 +844,8 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset) if (filter->pid && pid != filter->pid) capmt_pid_remove(capmt, adapter, filter->pid); filter->pid = pid; + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) + offset = 1; //filter data starts +1 byte because of adapter no memcpy(&filter->filter, sbuf_peek(sb, offset + 8), sizeof(filter->filter)); tvhlog_hexdump("capmt", filter->filter.filter, DMX_FILTER_SIZE); tvhlog_hexdump("capmt", filter->filter.mask, DMX_FILTER_SIZE); @@ -854,10 +879,15 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset) { uint8_t demux_index = sbuf_peek_u8 (sb, offset + 4); uint8_t filter_index = sbuf_peek_u8 (sb, offset + 5); - int16_t pid = sbuf_peek_s16be(sb, offset + 6); + int16_t pid; capmt_dmx_t *filter; capmt_filters_t *cf; + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) + pid = sbuf_peek_s16 (sb, offset + 6); + else + pid = sbuf_peek_s16be(sb, offset + 6); + tvhtrace("capmt", "%s: stopping filter: adapter=%d, demux=%d, filter=%d, pid=%d", capmt_name(capmt), adapter, demux_index, filter_index, pid); if (adapter >= MAX_CA || @@ -972,32 +1002,40 @@ static int capmt_msg_size(capmt_t *capmt, sbuf_t *sb, int offset) { uint32_t cmd; + uint8_t adapter_byte = 0; int oscam_new = capmt_oscam_new(capmt); if (sb->sb_ptr - offset < 4) return 0; cmd = sbuf_peek_u32(sb, offset); - if (!sb->sb_bswap && !sb->sb_err) { - if (cmd == CA_SET_PID_X || - cmd == CA_SET_DESCR_X || - cmd == CA_SET_DESCR_AES_X || - cmd == DMX_SET_FILTER_X || - cmd == DMX_STOP_X) { - sb->sb_bswap = 1; - cmd = sbuf_peek_u32(sb, offset); + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) { + adapter_byte = 1; //we need to take into account the adapter index byte which is now after the cmd + } else { + if (!sb->sb_bswap && !sb->sb_err) { + if (cmd == CA_SET_PID_X || + cmd == CA_SET_DESCR_X || + cmd == CA_SET_DESCR_AES_X || + cmd == DMX_SET_FILTER_X || + cmd == DMX_STOP_X) { + sb->sb_bswap = 1; + cmd = sbuf_peek_u32(sb, offset); + } } } sb->sb_err = 1; /* "first seen" flag for the moment */ if (cmd == CA_SET_PID) - return 4 + 8; + return 4 + 8 + adapter_byte; else if (cmd == CA_SET_DESCR) - return 4 + 16; + return 4 + 16 + adapter_byte; else if (cmd == CA_SET_DESCR_AES) return 4 + 32; else if (oscam_new && cmd == DMX_SET_FILTER) - return 4 + 2 + 60; + //when using network protocol the dmx_sct_filter_params fields are added seperately to avoid padding problems, so we substract 2 bytes: + return 4 + 2 + 60 + adapter_byte + (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? -2 : 0); else if (oscam_new && cmd == DMX_STOP) - return 4 + 4; + return 4 + 4 + adapter_byte; + else if (oscam_new && cmd == DVBAPI_SERVER_INFO && sb->sb_ptr > 6) + return 4 + 2 + 1 + sbuf_peek_u8(sb, 6); else { sb->sb_err = 0; return -1; /* fatal */ @@ -1012,6 +1050,11 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset) cmd = sbuf_peek_u32(sb, offset); + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO && cmd != DVBAPI_SERVER_INFO) { + adapter = sbuf_peek_u8(sb, 4); + offset = 1; + } + if (cmd == CA_SET_PID) { uint32_t seq = sbuf_peek_u32(sb, offset + 4); @@ -1088,13 +1131,23 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset) capmt_stop_filter(capmt, adapter, sb, offset); + } else if (cmd == DVBAPI_SERVER_INFO) { + + uint16_t protocol_version = sbuf_peek_u16(sb, offset + 4); + uint8_t len = sbuf_peek_u8(sb, offset + 4 + 2); + unsigned char oscam_info[len+1]; + memcpy(&oscam_info, sbuf_peek(sb, offset + 4 + 2 + 1), len); + oscam_info[len] = 0; //null-terminating the string + + tvhlog(LOG_INFO, "capmt", "%s: connected to %s, using network protocol_version = %d", capmt_name(capmt), oscam_info, protocol_version); + } } static void show_connection(capmt_t *capmt, const char *what) { - if (capmt->capmt_oscam == CAPMT_OSCAM_TCP) { + if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) { tvhlog(LOG_INFO, "capmt", "%s: mode %i connected to %s:%i (%s)", capmt_name(capmt), @@ -1222,7 +1275,7 @@ handle_ca0(capmt_t *capmt) { static void handle_single(capmt_t *capmt) { - int ret, recvsock, adapter, nfds, cmd_size, reconnect; + int ret, recvsock, adapter, nfds, cmd_size, reconnect, offset; uint8_t buf[256]; sbuf_t buffer; tvhpoll_event_t ev; @@ -1284,18 +1337,24 @@ handle_single(capmt_t *capmt) while (buffer.sb_ptr > 0) { cmd_size = 0; adapter = -1; + offset = 0; while (buffer.sb_ptr > 0) { + if (capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO) { + buffer.sb_bswap = 1; + } else { adapter = buffer.sb_data[0]; + offset = 1; + } if (adapter < MAX_CA) { - cmd_size = capmt_msg_size(capmt, &buffer, 1); + cmd_size = capmt_msg_size(capmt, &buffer, offset); if (cmd_size >= 0) break; } sbuf_cut(&buffer, 1); } - if (cmd_size + 1 <= buffer.sb_ptr) { - capmt_analyze_cmd(capmt, adapter, &buffer, 1); - sbuf_cut(&buffer, cmd_size + 1); + if (cmd_size + offset <= buffer.sb_ptr) { + capmt_analyze_cmd(capmt, adapter, &buffer, offset); + sbuf_cut(&buffer, cmd_size + offset); } else { break; } @@ -1402,6 +1461,7 @@ capmt_thread(void *aux) /* Accessible */ if (capmt->capmt_sockfile && capmt->capmt_oscam != CAPMT_OSCAM_TCP && + capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO && !access(capmt->capmt_sockfile, R_OK | W_OK)) caclient_set_status((caclient_t *)capmt, CACLIENT_STATUS_NONE); else @@ -1459,6 +1519,7 @@ capmt_thread(void *aux) } #else if (capmt->capmt_oscam == CAPMT_OSCAM_TCP || + capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO || capmt->capmt_oscam == CAPMT_OSCAM_UNIX_SOCKET) { handle_single(capmt); } else { @@ -1817,6 +1878,7 @@ capmt_service_start(caclient_t *cac, service_t *s) goto fin; if (tuner < 0 && capmt->capmt_oscam != CAPMT_OSCAM_TCP && + capmt->capmt_oscam != CAPMT_OSCAM_NET_PROTO && capmt->capmt_oscam != CAPMT_OSCAM_UNIX_SOCKET) { tvhlog(LOG_WARNING, "capmt", "%s: Virtual adapters are supported only in modes 3 and 4 (service \"%s\")", @@ -1920,7 +1982,7 @@ capmt_free(caclient_t *cac) tvhlog(LOG_INFO, "capmt", "%s: mode %i %s %s port %i destroyed", capmt_name(capmt), capmt->capmt_oscam, - capmt->capmt_oscam == CAPMT_OSCAM_TCP ? "IP address" : "sockfile", + capmt->capmt_oscam == CAPMT_OSCAM_TCP || capmt->capmt_oscam == CAPMT_OSCAM_NET_PROTO ? "IP address" : "sockfile", capmt->capmt_sockfile, capmt->capmt_port); capmt_flush_queue(capmt, 1); free(capmt->capmt_sockfile); @@ -1971,11 +2033,12 @@ static htsmsg_t * caclient_capmt_class_oscam_mode_list ( void *o ) { static const struct strtab tab[] = { - { "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET}, - { "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP }, - { "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST }, - { "Older OSCam", CAPMT_OSCAM_OLD }, - { "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER }, + { "OSCam net protocol (rev >= 10087)", CAPMT_OSCAM_NET_PROTO}, + { "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET}, + { "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP }, + { "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST }, + { "Older OSCam", CAPMT_OSCAM_OLD }, + { "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER }, }; return strtab2htsmsg(tab); }