tsdecrypt reads and decrypts CSA encrypted incoming mpeg transport stream over UDP/RTP using code words obtained from OSCAM or similar CAM server. tsdecrypt communicates with CAM server using cs378x (camd35 over tcp) protocol or newcamd protocol. https://georgi.unixsol.org/programs/tsdecrypt/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tsdecrypt.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*
  2. * tsdecrypt
  3. * Copyright (C) 2011 Unix Solutions Ltd.
  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 version 2
  7. * as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  17. */
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <signal.h>
  24. #include <fcntl.h>
  25. #include <errno.h>
  26. #include "data.h"
  27. #include "util.h"
  28. #include "camd.h"
  29. #include "process.h"
  30. #include "udp.h"
  31. #define PROGRAM_NAME "tsdecrypt"
  32. static const char *program_id = PROGRAM_NAME " " GIT_VER " build " BUILD_ID;
  33. static int keep_running = 1;
  34. static void LOG_func(const char *msg) {
  35. char date[64];
  36. struct tm tm;
  37. time_t now;
  38. now = time(NULL);
  39. localtime_r(&now, &tm);
  40. strftime(date, sizeof(date), "%F %H:%M:%S", localtime(&now));
  41. fprintf(stderr, "%s | %s", date, msg);
  42. }
  43. static void show_help(struct ts *ts) {
  44. printf("%s\n", program_id);
  45. printf("Copyright (c) 2011 Unix Solutions Ltd.\n");
  46. printf("\n");
  47. printf(" Usage: " PROGRAM_NAME " [opts]\n");
  48. printf("\n");
  49. printf(" Daemon options:\n");
  50. printf(" -i server_ident | Format PROVIDER/CHANNEL (default: %s)\n", ts->ident);
  51. printf(" -d pidfile | Daemonize program with pid file. (default: do not daemonize)\n");
  52. printf(" -l syslog host | Where is the syslog server (default: disabled)\n");
  53. printf(" -L Syslog port | What is the syslog server port (default: %d)\n", ts->syslog_port);
  54. printf("\n");
  55. printf(" Input options:\n");
  56. printf(" -I input | Where to read from. Supports files and multicast\n");
  57. printf(" | -I 224.0.0.1:5000 (multicast receive)\n");
  58. printf(" | -I file.ts (read from file)\n");
  59. printf(" | -I - (read from STDIN, the default)\n");
  60. printf(" -R | Enable RTP input\n");
  61. printf("\n");
  62. printf(" -c ca_system | default: %s valid: IRDETO, CONAX, CRYPTOWORKS\n", ts_get_CA_sys_txt(ts->req_CA_sys));
  63. printf(" -z | Detect discontinuty errors in input stream (default: %s).\n", ts->ts_discont ? "report" : "ignore");
  64. printf("\n");
  65. printf(" Output options:\n");
  66. printf(" -O output | Where to send output. Supports files and multicast\n");
  67. printf(" | -O 239.0.0.1:5000 (multicast send)\n");
  68. printf(" | -O file.ts (write to file)\n");
  69. printf(" | -O - (write to STDOUT, the default)\n");
  70. printf("\n");
  71. printf(" -o output_intf | default: %s\n", inet_ntoa(ts->output.intf));
  72. printf(" -t output_ttl | default: %d\n", ts->output.ttl);
  73. printf("\n");
  74. printf(" CAMD35 server options:\n");
  75. printf(" -s server_addr | default: disabled (format 1.2.3.4:2233)\n");
  76. printf(" -U server_user | default: %s\n", ts->camd35.user);
  77. printf(" -P server_pass | default: %s\n", ts->camd35.pass);
  78. printf(" -y usec_delay | Sleep X usec between sending ECM/EMM packets to OSCAM. Default: %d\n", ts->packet_delay);
  79. printf("\n");
  80. printf(" ECM options:\n");
  81. printf(" -G ecm_type | IRDETO: Process only ECMs with selected type (0,1,2,3). Default: %d\n", ts->irdeto_ecm);
  82. printf("\n");
  83. printf(" EMM options:\n");
  84. printf(" -E | Process only EMMs without decoding input stream. Default: %s\n", ts->emm_only ? "true" : "false");
  85. printf(" -f <seconds> | Report how much EMMs has been send for processing each X seconds.\n");
  86. printf(" | each <seconds> seconds. Set to 0 to disable reporting. Default: %d\n", ts->camd35.emm_count_report_interval);
  87. printf("\n");
  88. printf(" Filtering options:\n");
  89. printf(" -e | EMM send (default: %s).\n", ts->emm_send ? "enabled" : "disabled");
  90. printf(" | - Send EMMs to CAMD server for processing.\n");
  91. printf("\n");
  92. printf(" -p | Output PID filter (default: %s).\n", ts->pid_filter ? "enabled" : "disabled");
  93. printf(" | - When PID filter is enabled only PAT/PMT/SDT/data\n");
  94. printf(" | - packets are left in the output.\n");
  95. printf("\n");
  96. printf(" -D debug_level | Message debug level.\n");
  97. printf(" | 0 - default messages\n");
  98. printf(" | 1 - show PSI tables\n");
  99. printf(" | 2 - show EMMs\n");
  100. printf(" | 3 - show duplicate ECMs\n");
  101. printf(" | 4 - packet debug\n");
  102. printf("\n");
  103. }
  104. static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_mode) {
  105. io->type = WTF_IO;
  106. char *p = strrchr(opt, ':');
  107. if (!p) {
  108. io->type = FILE_IO;
  109. if (strcmp(opt, "-") != 0) {
  110. io->fd = open(opt, open_flags, open_mode);
  111. if (io->fd < 0) {
  112. fprintf(stderr, "ERROR: Can not open file (%s): %s\n", opt, strerror(errno));
  113. exit(1);
  114. }
  115. }
  116. io->fname = strdup(opt);
  117. return 0;
  118. }
  119. *p = 0x00;
  120. io->type = NET_IO;
  121. io->port = atoi(p + 1);
  122. if (inet_aton(opt, &io->addr) == 0)
  123. return 1;
  124. return 0;
  125. }
  126. static void parse_options(struct ts *ts, int argc, char **argv) {
  127. int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0;
  128. while ((j = getopt(argc, argv, "i:d:l:L:c:s:I:O:o:t:U:P:y:f:eEzpD:hRG:")) != -1) {
  129. char *p = NULL;
  130. switch (j) {
  131. case 'i':
  132. strncpy(ts->ident, optarg, sizeof(ts->ident) - 1);
  133. ts->ident[sizeof(ts->ident) - 1] = 0;
  134. break;
  135. case 'd':
  136. strncpy(ts->pidfile, optarg, sizeof(ts->pidfile) - 1);
  137. ts->pidfile[sizeof(ts->pidfile) - 1] = 0;
  138. ts->daemonize = 1;
  139. break;
  140. case 'l':
  141. strncpy(ts->syslog_host, optarg, sizeof(ts->syslog_host) - 1);
  142. ts->syslog_host[sizeof(ts->syslog_host) - 1] = 0;
  143. ts->syslog_active = 1;
  144. break;
  145. case 'L':
  146. ts->syslog_port = atoi(optarg);
  147. break;
  148. case 'c':
  149. if (strcasecmp("IRDETO", optarg) == 0)
  150. ts->req_CA_sys = CA_IRDETO;
  151. else if (strcasecmp("CONNAX", optarg) == 0 || strcasecmp("CONAX", optarg) == 0)
  152. ts->req_CA_sys = CA_CONAX;
  153. else if (strcasecmp("CRYPTOWORKS", optarg) == 0)
  154. ts->req_CA_sys = CA_CRYPTOWORKS;
  155. else
  156. ca_err = 1;
  157. break;
  158. case 's':
  159. p = strrchr(optarg, ':');
  160. if (p) {
  161. *p = 0x00;
  162. ts->camd35.server_port = atoi(p + 1);
  163. }
  164. if (inet_aton(optarg, &ts->camd35.server_addr) == 0)
  165. server_err = 1;
  166. else
  167. server_err = 0;
  168. break;
  169. case 'I':
  170. input_addr_err = parse_io_param(&ts->input, optarg, O_RDONLY, 0);
  171. break;
  172. case 'R':
  173. ts->rtp_input = !ts->rtp_input;
  174. break;
  175. case 'O':
  176. output_addr_err = parse_io_param(&ts->output, optarg,
  177. O_CREAT | O_WRONLY | O_TRUNC,
  178. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  179. break;
  180. case 'o':
  181. if (inet_aton(optarg, &ts->output.intf) == 0)
  182. output_intf_err = 1;
  183. break;
  184. case 't':
  185. ts->output.ttl = atoi(optarg);
  186. break;
  187. case 'G':
  188. ts->irdeto_ecm = atoi(optarg);
  189. break;
  190. case 'U':
  191. strncpy(ts->camd35.user, optarg, sizeof(ts->camd35.user) - 1);
  192. ts->camd35.user[sizeof(ts->camd35.user) - 1] = 0;
  193. break;
  194. case 'P':
  195. strncpy(ts->camd35.pass, optarg, sizeof(ts->camd35.pass) - 1);
  196. ts->camd35.pass[sizeof(ts->camd35.pass) - 1] = 0;
  197. break;
  198. case 'y':
  199. ts->packet_delay = atoi(optarg);
  200. if (ts->packet_delay < 0 || ts->packet_delay > 1000000)
  201. ts->packet_delay = 0;
  202. break;
  203. case 'z':
  204. ts->ts_discont = !ts->ts_discont;
  205. break;
  206. case 'f':
  207. ts->camd35.emm_count_report_interval = atoi(optarg);
  208. if (ts->camd35.emm_count_report_interval < 0)
  209. ts->camd35.emm_count_report_interval = 0;
  210. if (ts->camd35.emm_count_report_interval > 86400)
  211. ts->camd35.emm_count_report_interval = 86400;
  212. break;
  213. case 'e':
  214. ts->emm_send = !ts->emm_send;
  215. break;
  216. case 'E':
  217. ts->emm_only = 1;
  218. ts->emm_send = 1;
  219. break;
  220. case 'p':
  221. ts->pid_filter = !ts->pid_filter;
  222. break;
  223. case 'D':
  224. ts->debug_level = atoi(optarg);
  225. break;
  226. case 'h':
  227. show_help(ts);
  228. exit(0);
  229. }
  230. }
  231. if (ts->syslog_active && !ts->ident[0])
  232. ident_err = 1;
  233. if (ident_err || ca_err || server_err || input_addr_err || output_addr_err || ts->input.type == WTF_IO || ts->output.type == WTF_IO) {
  234. show_help(ts);
  235. if (ident_err)
  236. fprintf(stderr, "ERROR: Syslog is enabled but ident was not set.\n");
  237. if (ca_err)
  238. fprintf(stderr, "ERROR: Requested CA system is unsupported.\n");
  239. if (server_err)
  240. fprintf(stderr, "ERROR: Server IP address is not set or it is invalid.\n");
  241. if (input_addr_err)
  242. fprintf(stderr, "ERROR: Input IP address is invalid.\n");
  243. if (output_addr_err)
  244. fprintf(stderr, "ERROR: Output IP address is invalid.\n");
  245. if (output_intf_err)
  246. fprintf(stderr, "ERROR: Output interface address is invalid.\n");
  247. exit(1);
  248. }
  249. if (ts->ident[0])
  250. ts_LOGf("Ident : %s\n", ts->ident);
  251. else
  252. ts_LOGf("Ident : *NOT SET*\n");
  253. if (ts->pidfile[0])
  254. ts_LOGf("Daemonize : %s pid file.\n", ts->pidfile);
  255. else
  256. ts_LOGf("Daemonize : no daemon\n");
  257. if (ts->syslog_active)
  258. ts_LOGf("Syslog : %s:%d\n", ts->syslog_host, ts->syslog_port);
  259. else
  260. ts_LOGf("Syslog : disabled\n");
  261. ts_LOGf("CA System : %s\n", ts_get_CA_sys_txt(ts->req_CA_sys));
  262. if (ts->input.type == NET_IO) {
  263. ts_LOGf("Input addr : %s://%s:%u/\n",
  264. ts->rtp_input ? "rtp" : "udp",
  265. inet_ntoa(ts->input.addr), ts->input.port);
  266. } else if (ts->input.type == FILE_IO) {
  267. ts_LOGf("Input file : %s\n", ts->input.fd == 0 ? "STDIN" : ts->input.fname);
  268. }
  269. if (ts->req_CA_sys == CA_IRDETO)
  270. ts_LOGf("Irdeto ECM : %d\n", ts->irdeto_ecm);
  271. if (!ts->emm_only)
  272. {
  273. if (ts->output.type == NET_IO) {
  274. ts_LOGf("Output addr: udp://%s:%u/\n", inet_ntoa(ts->output.addr), ts->output.port);
  275. ts_LOGf("Output intf: %s\n", inet_ntoa(ts->output.intf));
  276. ts_LOGf("Output ttl : %d\n", ts->output.ttl);
  277. } else if (ts->output.type == FILE_IO) {
  278. ts_LOGf("Output file: %s\n", ts->output.fd == 1 ? "STDOUT" : ts->output.fname);
  279. }
  280. }
  281. ts_LOGf("Server addr: tcp://%s:%u/\n", inet_ntoa(ts->camd35.server_addr), ts->camd35.server_port);
  282. ts_LOGf("Server user: %s\n", ts->camd35.user);
  283. ts_LOGf("Server pass: %s\n", ts->camd35.pass);
  284. if (ts->packet_delay)
  285. ts_LOGf("Pkt sleep : %d us (%d ms)\n", ts->packet_delay, ts->packet_delay / 1000);
  286. ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
  287. ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
  288. if (ts->emm_send && ts->camd35.emm_count_report_interval)
  289. ts_LOGf("EMM report : %d sec\n", ts->camd35.emm_count_report_interval);
  290. if (ts->emm_send && ts->camd35.emm_count_report_interval == 0)
  291. ts_LOGf("EMM report : disabled\n");
  292. if (ts->emm_only) {
  293. ts_LOGf("EMM only : %s\n", ts->emm_only ? "yes" : "no");
  294. } else {
  295. ts_LOGf("EMM send : %s\n", ts->emm_send ? "enabled" : "disabled");
  296. ts_LOGf("PID filter : %s\n", ts->pid_filter ? "enabled" : "disabled");
  297. ts_LOGf("Decoding : %s\n", ts->threaded ? "threaded" : "single thread");
  298. }
  299. for (i=0; i<(int)sizeof(ts->ident); i++) {
  300. if (!ts->ident[i])
  301. break;
  302. if (ts->ident[i] == '/')
  303. ts->ident[i] = '-';
  304. }
  305. }
  306. void signal_quit(int sig) {
  307. if (!keep_running)
  308. raise(sig);
  309. keep_running = 0;
  310. ts_LOGf("Killed %s with signal %d\n", program_id, sig);
  311. signal(sig, SIG_DFL);
  312. }
  313. #define RTP_HDR_SZ 12
  314. int main(int argc, char **argv) {
  315. ssize_t readen;
  316. uint8_t ts_packet[FRAME_SIZE + RTP_HDR_SZ];
  317. uint8_t rtp_hdr[2][RTP_HDR_SZ];
  318. int rtp_hdr_pos = 0, num_packets = 0;
  319. struct ts ts;
  320. memset(rtp_hdr[0], 0, RTP_HDR_SZ);
  321. memset(rtp_hdr[1], 0, RTP_HDR_SZ);
  322. data_init(&ts);
  323. parse_options(&ts, argc, argv);
  324. if (ts.pidfile[0])
  325. daemonize(ts.pidfile);
  326. if (!ts.syslog_active) {
  327. ts_set_log_func(LOG_func);
  328. } else {
  329. ts_set_log_func(LOG);
  330. log_init(ts.ident, ts.syslog_active, ts.daemonize != 1, ts.syslog_host, ts.syslog_port);
  331. }
  332. ts_LOGf("Start %s\n", program_id);
  333. if (ts.input.type == NET_IO && udp_connect_input(&ts.input) < 1)
  334. goto EXIT;
  335. if (ts.output.type == NET_IO && udp_connect_output(&ts.output) < 1)
  336. goto EXIT;
  337. signal(SIGCHLD, SIG_IGN);
  338. signal(SIGPIPE, SIG_IGN);
  339. signal(SIGINT , signal_quit);
  340. signal(SIGTERM, signal_quit);
  341. if (&ts.threaded) {
  342. pthread_create(&ts.decode_thread, NULL, &decode_thread, &ts);
  343. pthread_create(&ts.write_thread, NULL , &write_thread , &ts);
  344. }
  345. camd_start(&ts);
  346. do {
  347. if (ts.input.type == NET_IO) {
  348. if (!ts.rtp_input) {
  349. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE, 250, 4, 1);
  350. } else {
  351. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE + RTP_HDR_SZ, 250, 4, 1);
  352. if (readen > RTP_HDR_SZ) {
  353. memcpy(rtp_hdr[rtp_hdr_pos], ts_packet, RTP_HDR_SZ);
  354. memmove(ts_packet, ts_packet + RTP_HDR_SZ, FRAME_SIZE);
  355. readen -= RTP_HDR_SZ;
  356. uint16_t ssrc = (rtp_hdr[rtp_hdr_pos][2] << 8) | rtp_hdr[rtp_hdr_pos][3];
  357. uint16_t pssrc = (rtp_hdr[!rtp_hdr_pos][2] << 8) | rtp_hdr[!rtp_hdr_pos][3];
  358. rtp_hdr_pos = !rtp_hdr_pos;
  359. if (pssrc + 1 != ssrc && (ssrc != 0 && pssrc != 0xffff) && num_packets > 2)
  360. if (ts.ts_discont)
  361. ts_LOGf("--- | RTP discontinuity last_ssrc %5d, curr_ssrc %5d, lost %d packet\n",
  362. pssrc, ssrc, ((ssrc - pssrc)-1) & 0xffff);
  363. num_packets++;
  364. }
  365. }
  366. } else {
  367. readen = read(ts.input.fd, ts_packet, FRAME_SIZE);
  368. }
  369. if (readen > 0)
  370. process_packets(&ts, ts_packet, readen);
  371. if (!keep_running)
  372. break;
  373. } while (readen > 0);
  374. EXIT:
  375. camd_stop(&ts);
  376. if (ts.threaded) {
  377. ts.decode_stop = 1;
  378. ts.write_stop = 1;
  379. pthread_join(ts.decode_thread, NULL);
  380. pthread_join(ts.write_thread, NULL);
  381. }
  382. data_free(&ts);
  383. ts_LOGf("Stop %s\n", program_id);
  384. if (ts.syslog_active)
  385. log_close();
  386. if (ts.daemonize)
  387. unlink(ts.pidfile);
  388. exit(0);
  389. }