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 13KB

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