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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. /*
  2. * tsdecrypt
  3. * Copyright (C) 2011-2012 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 (COPYING file) for more details.
  13. *
  14. */
  15. #include <stdlib.h>
  16. #include <unistd.h>
  17. #include <getopt.h>
  18. #include <string.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <signal.h>
  22. #include <fcntl.h>
  23. #include <errno.h>
  24. #include <syslog.h>
  25. #include <sys/resource.h>
  26. #include "libfuncs/libfuncs.h"
  27. #include "data.h"
  28. #include "util.h"
  29. #include "csa.h"
  30. #include "camd.h"
  31. #include "process.h"
  32. #include "udp.h"
  33. #include "notify.h"
  34. #include "filter.h"
  35. #define FIRST_REPORT_SEC 3
  36. #define PROGRAM_NAME "tsdecrypt"
  37. static const char *program_id = PROGRAM_NAME " v" VERSION " (" GIT_VER ", " DLIB ")";
  38. int keep_running = 1;
  39. static FILE *log_file = NULL;
  40. static char *log_filename = NULL;
  41. static int local_syslog = 0;
  42. static int remote_syslog = 0;
  43. static int packet_from_file = 0;
  44. static int packet_buflen;
  45. static uint8_t packet_buf[256];
  46. static enum msg_type packet_type = ECM_MSG;
  47. extern int ai_family;
  48. static void do_log(FILE *f, time_t now, const char *msg) {
  49. char date[64];
  50. struct tm tm;
  51. // There is no need to show timestamps when debug options are used
  52. if (packet_from_file) {
  53. fprintf(f, "%s", msg);
  54. return;
  55. }
  56. localtime_r(&now, &tm);
  57. strftime(date, sizeof(date), "%F %H:%M:%S", localtime_r(&now, &tm));
  58. fprintf(f, "%s | %s", date, msg);
  59. }
  60. static void LOG_func(const char *msg) {
  61. time_t now = time(NULL);
  62. do_log(stderr, now, msg);
  63. if (log_file)
  64. do_log(log_file, now, msg);
  65. if (local_syslog)
  66. syslog(LOG_INFO, msg, strlen(msg));
  67. if (remote_syslog)
  68. LOG(msg);
  69. }
  70. static const char short_options[] = "i:d:N:90:Sl:L:F:I:1:RzM:T:W:O:o:t:rk:g:upwx3yc:C:Y:Q:A:s:U:P:B:46eZ:Ef:a:X:vqH:G:2:KJ:D:jbhVn:m:";
  71. // Unused short options: 578
  72. static const struct option long_options[] = {
  73. { "ident", required_argument, NULL, 'i' },
  74. { "daemon", required_argument, NULL, 'd' },
  75. { "syslog", no_argument, NULL, 'S' },
  76. { "syslog-host", required_argument, NULL, 'l' },
  77. { "syslog-port", required_argument, NULL, 'L' },
  78. { "log-file", required_argument, NULL, 'F' },
  79. { "notify-program", required_argument, NULL, 'N' },
  80. { "notify-wait", no_argument, NULL, '9' },
  81. { "status-file", required_argument, NULL, '0' },
  82. { "input", required_argument, NULL, 'I' },
  83. { "input-source", required_argument, NULL, '1' },
  84. { "input-rtp", no_argument, NULL, 'R' },
  85. { "input-ignore-disc", no_argument, NULL, 'z' },
  86. { "input-service", required_argument, NULL, 'M' },
  87. { "input-buffer", required_argument, NULL, 'T' },
  88. { "input-dump", required_argument, NULL, 'W' },
  89. { "output", required_argument, NULL, 'O' },
  90. { "output-intf", required_argument, NULL, 'o' },
  91. { "output-ttl", required_argument, NULL, 't' },
  92. { "output-rtp", no_argument, NULL, 'r' },
  93. { "output-rtp-ssrc", required_argument, NULL, 'k' },
  94. { "output-tos", required_argument, NULL, 'g' },
  95. { "no-output-on-error", no_argument, NULL, 'u' },
  96. { "no-output-filter", no_argument, NULL, 'p' },
  97. { "output-nit-pass", no_argument, NULL, 'y' },
  98. { "output-eit-pass", no_argument, NULL, 'w' },
  99. { "output-tdt-pass", no_argument, NULL, 'x' },
  100. { "output-enc-pass", no_argument, NULL, '3' },
  101. { "ca-system", required_argument, NULL, 'c' },
  102. { "caid", required_argument, NULL, 'C' },
  103. { "const-cw", required_argument, NULL, 'Y' },
  104. { "biss-key", required_argument, NULL, 'Q' },
  105. { "camd-proto", required_argument, NULL, 'A' },
  106. { "camd-server", required_argument, NULL, 's' },
  107. { "camd-user", required_argument, NULL, 'U' },
  108. { "camd-pass", required_argument, NULL, 'P' },
  109. { "camd-des-key", required_argument, NULL, 'B' },
  110. { "ipv4", no_argument, NULL, '4' },
  111. { "ipv6", no_argument, NULL, '6' },
  112. { "emm", no_argument, NULL, 'e' },
  113. { "emm-pid", required_argument, NULL, 'Z' },
  114. { "emm-only", no_argument, NULL, 'E' },
  115. { "emm-report-time", required_argument, NULL, 'f' },
  116. { "emm-filter", required_argument, NULL, 'a' },
  117. { "ecm-pid", required_argument, NULL, 'X' },
  118. { "ecm-only", no_argument, NULL, 'v' },
  119. { "ecm-report-time", required_argument, NULL, 'H' },
  120. { "ecm-irdeto-type", required_argument, NULL, 'G' },
  121. { "ecm-irdeto-chid", required_argument, NULL, '2' },
  122. { "ecm-no-log", no_argument , NULL, 'K' },
  123. { "cw-warn-time", required_argument, NULL, 'J' },
  124. { "ecm-and-emm-only", no_argument, NULL, 'q' },
  125. { "debug", required_argument, NULL, 'D' },
  126. { "pid-report", no_argument, NULL, 'j' },
  127. { "bench", no_argument, NULL, 'b' },
  128. { "help", no_argument, NULL, 'h' },
  129. { "version", no_argument, NULL, 'V' },
  130. { "ecm-file", required_argument, NULL, 'n' },
  131. { "emm-file", required_argument, NULL, 'm' },
  132. { 0, 0, 0, 0 }
  133. };
  134. static void show_help(struct ts *ts) {
  135. printf("%s\n", program_id);
  136. printf("Copyright (C) 2011-2012 Unix Solutions Ltd.\n");
  137. printf("\n");
  138. printf(" Usage: " PROGRAM_NAME " [opts]\n");
  139. printf("\n");
  140. printf("Main options:\n");
  141. printf(" -i --ident <server> | Format PROVIDER/CHANNEL. Default: empty\n");
  142. printf(" -d --daemon <pidfile> | Daemonize program and write pid file.\n");
  143. printf(" -N --notify-program <prg> | Execute <prg> to report events. Default: empty\n");
  144. printf(" -9 --notify-wait | Enable one by one notification delivery.\n");
  145. printf(" . Default: not set (async, deliver ASAP)\n");
  146. printf(" -0 --status-file <file> | Save current program status in file.\n");
  147. printf("\n");
  148. printf("Input options:\n");
  149. printf(" -I --input <source> | Where to read from. File or multicast address.\n");
  150. printf(" . -I 224.0.0.1:5000 (v4 multicast)\n");
  151. printf(" . -I [ff01::1111]:5000 (v6 multicast)\n");
  152. printf(" . -I file://in.ts (read from file)\n");
  153. printf(" . By default the input is stdin.\n");
  154. printf(" -1 --input-source <ipaddr> | Set multicast input source ip.\n");
  155. printf(" -R --input-rtp | Enable RTP input\n");
  156. printf(" -z --input-ignore-disc | Do not report discontinuty errors in input.\n");
  157. printf(" -M --input-service <srvid> | Choose service id when input is MPTS.\n");
  158. printf(" -T --input-buffer <ms> | Set input buffer time in ms. Default: %u\n", ts->input_buffer_time);
  159. printf(" -W --input-dump <filename> | Save input stream in file.\n");
  160. printf("\n");
  161. printf("Output options:\n");
  162. printf(" -O --output <dest> | Where to send output. File or multicast address.\n");
  163. printf(" . -O 239.0.0.1:5000 (v4 multicast)\n");
  164. printf(" . -O [ff01::2222]:5000 (v6 multicast)\n");
  165. printf(" . -O file://out.ts (write to file)\n");
  166. printf(" . By default the output is stdout.\n");
  167. printf(" -o --output-intf <value> | Set multicast output interface.\n");
  168. printf(" . Default for IPv4: 0.0.0.0 (intf addr)\n");
  169. printf(" . Default for IPv6: -1 (intf number)\n");
  170. printf(" -t --output-ttl <ttl> | Set multicast ttl. Default: %d\n", ts->output.ttl);
  171. printf(" -r --output-rtp | Enable RTP output.\n");
  172. printf(" -k --output-rtp-ssrc <id> | Set RTP SSRC. Default: %u\n", ts->rtp_ssrc);
  173. printf(" -g --output-tos <tos> | Set TOS value of output packets. Default: none\n");
  174. printf(" -u --no-output-on-error | Do not output data when the code word is missing.\n");
  175. printf(" -p --no-output-filter | Disable output filtering. Default: %s\n", ts->pid_filter ? "enabled" : "disabled");
  176. printf(" -3 --output-enc-pass | Output the stream even if can not be decrypted.\n");
  177. printf(" -y --output-nit-pass | Pass through NIT.\n");
  178. printf(" -w --output-eit-pass | Pass through EIT (EPG).\n");
  179. printf(" -x --output-tdt-pass | Pass through TDT/TOT.\n");
  180. printf("\n");
  181. printf("CA options:\n");
  182. printf(" -c --ca-system <ca_sys> | Process input EMM/ECM from <ca_sys>.\n");
  183. printf(" | Valid systems are: CONAX (default), CRYPTOWORKS,\n");
  184. printf(" . IRDETO, SECA (MEDIAGUARD), VIACCESS,\n");
  185. printf(" . VIDEOGUARD (NDS), NAGRA, DRECRYPT, BULCRYPT,\n");
  186. printf(" . GRIFFIN and DGCRYPT.\n");
  187. printf(" -C --caid <caid> | Set CAID. Default: Taken from --ca-system.\n");
  188. printf(" -Y --const-cw <codeword> | Set constant code word for decryption.\n");
  189. printf(" . Example cw: a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8\n");
  190. printf(" -Q --biss-key <biss-key> | Set BISS key for decryption.\n");
  191. printf(" . Example key: 112233445566\n");
  192. printf("\n");
  193. printf("CAMD server options:\n");
  194. printf(" -A --camd-proto <proto> | Set CAMD network protocol.\n");
  195. printf(" . Valid protocols are: CS378X (default) and NEWCAMD\n");
  196. printf(" -s --camd-server <host> | Set CAMD server address. Default port: 2233\n");
  197. printf(" . Example IPv4 addr and port: 127.0.0.1:2233\n");
  198. printf(" . Example IPv6 addr and port: [2a00::1014]:2233\n");
  199. printf(" . Example hostname : example.com\n");
  200. printf(" . Example hostname and port : example.com:2233\n");
  201. printf(" . Example IPv4 hostname : ipv4.google.com\n");
  202. printf(" . Example IPv6 hostname : ipv6.google.com\n");
  203. printf(" -U --camd-user <user> | Set CAMD server user. Default: %s\n", ts->camd.user);
  204. printf(" -P --camd-pass <pass> | Set CAMD server password. Default: %s\n", ts->camd.pass);
  205. printf(" -B --camd-des-key <key> | Set DES key for newcamd protocol.\n");
  206. printf(" . Default: %s\n", ts->camd.newcamd.hex_des_key);
  207. printf(" -4 --ipv4 | Use only IPv4 addresses of the camd server.\n");
  208. printf(" -6 --ipv6 | Use only IPv6 addresses of the camd server.\n");
  209. printf("\n");
  210. printf("EMM options:\n");
  211. printf(" -e --emm | Enable sending EMM's to CAMD. Default: %s\n", ts->process_emm ? "enabled" : "disabled");
  212. printf(" -E --emm-only | Send only EMMs to CAMD, skipping ECMs and without\n");
  213. printf(" . decoding the input stream.\n");
  214. printf(" -Z --emm-pid <pid> | Force EMM pid. Default: none\n");
  215. printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
  216. printf(" . received/processed. Set <sec> to 0 to disable\n");
  217. printf(" . the reports. Default: %d sec\n", ts->emm_report_interval);
  218. printf(" -a --emm-filter <filter> | Add EMM filter defined by <filter>.\n");
  219. printf(" . This option can be used multiple times (max:%u).\n", MAX_FILTERS);
  220. printf(" . See FILTERING file for more info.\n");
  221. printf("\n");
  222. printf("ECM options:\n");
  223. printf(" -X --ecm-pid <pid> | Force ECM pid. Default: none\n");
  224. printf(" -v --ecm-only | Send only ECMs to CAMD, skipping EMMs and without\n");
  225. printf(" . decoding the input stream.\n");
  226. printf(" -H --ecm-report-time <sec> | Report each <sec> how much ECMs and CWs have been\n");
  227. printf(" . processed/skipped. Set <sec> to 0 to disable\n");
  228. printf(" . the reports. Default: %d sec\n", ts->ecm_report_interval);
  229. printf(" -G --ecm-irdeto-type <int> | Process IRDETO ECMs with index X /0-255/\n");
  230. printf(" . It is better to use --ecm-irdeto-chid option!\n");
  231. printf(" -2 --ecm-irdeto-chid <int> | Set CHID to filter Irdeto ECMs (Default: 0x0000).\n");
  232. printf(" -K --ecm-no-log | Disable ECM and code words logging.\n");
  233. printf(" -J --cw-warn-time <sec> | Warn if no valid code word has been received.\n");
  234. printf(" . Set <sec> to 0 to disable. Default: %d sec\n", ts->cw_warn_sec);
  235. printf("\n");
  236. printf(" -q --ecm-and-emm-only | Send ECMs and EMMs to CAMD but do not decode\n");
  237. printf(" . the input stream.\n");
  238. printf("\n");
  239. printf("Logging options:\n");
  240. printf(" -S --syslog | Log messages using syslog.\n");
  241. printf(" -l --syslog-host <host> | Syslog server address. Default: disabled\n");
  242. printf(" -L --syslog-port <port> | Syslog server port. Default: %d\n", ts->syslog_port);
  243. printf(" -F --log-file <filename> | Log to file <filename>.\n");
  244. printf(" -D --debug <level> | Message debug level.\n");
  245. printf(" . 0 = default messages\n");
  246. printf(" . 1 = show PSI tables\n");
  247. printf(" . 2 = show EMMs\n");
  248. printf(" . 3 = show duplicate ECMs\n");
  249. printf(" . 4 = packet debug\n");
  250. printf(" . 5 = packet debug + packet dump\n");
  251. printf("\n");
  252. printf("Debugging options:\n");
  253. printf(" -n --ecm-file <file.txt> | Read ECM from text file.\n");
  254. printf(" -m --emm-file <file.txt> | Read EMM from text file.\n");
  255. printf("\n");
  256. printf("Misc options:\n");
  257. printf(" -j --pid-report | Report how much packets were received.\n");
  258. printf(" -b --bench | Benchmark decrypton.\n");
  259. printf(" -h --help | Show help screen.\n");
  260. printf(" -V --version | Show program version.\n");
  261. printf("\n");
  262. }
  263. static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_mode) {
  264. int port_set = 0, host_set;
  265. io->type = WTF_IO;
  266. if (strstr(opt, "file://") == opt) {
  267. io->fname = opt + 7; // strlen("file://")
  268. io->type = FILE_IO;
  269. } else if (strchr(opt, '/')) {
  270. io->fname = opt;
  271. io->type = FILE_IO;
  272. }
  273. if (io->type == FILE_IO) {
  274. io->fd = open(io->fname, open_flags, open_mode);
  275. if (io->fd < 0) {
  276. fprintf(stderr, "ERROR: Can not open file (%s): %s\n", io->fname, strerror(errno));
  277. exit(EXIT_FAILURE);
  278. }
  279. return 1;
  280. }
  281. io->type = NET_IO;
  282. host_set = parse_host_and_port(opt, &io->hostname, &io->service, &port_set);
  283. return !(!port_set || !host_set);
  284. }
  285. static void parse_options(struct ts *ts, int argc, char **argv) {
  286. int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, ident_err = 0, port_set = 0;
  287. while ((j = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
  288. if (j == '?')
  289. exit(EXIT_FAILURE);
  290. switch (j) {
  291. case 'i': // -- ident
  292. ts->ident = optarg;
  293. break;
  294. case 'd': // --daemon
  295. ts->pidfile = optarg;
  296. break;
  297. case 'N': // --notify-program
  298. ts->notify_program = optarg;
  299. break;
  300. case '9': // --notify-wait
  301. ts->notify_wait = !ts->notify_wait;
  302. break;
  303. case '0': // --status-file
  304. ts->status_file = optarg;
  305. ts->status_file_tmp = calloc(1, strlen(optarg) + 16);
  306. snprintf(ts->status_file_tmp, strlen(optarg) + 16, "%s.tmp", optarg);
  307. break;
  308. case 'S': // --syslog
  309. ts->syslog_active = 1;
  310. ts->syslog_remote = 0;
  311. break;
  312. case 'l': // --syslog-host
  313. ts->syslog_host = optarg;
  314. ts->syslog_active = 1;
  315. ts->syslog_remote = 1;
  316. break;
  317. case 'L': // --syslog-port
  318. ts->syslog_port = atoi(optarg);
  319. break;
  320. case 'F': // --log-file
  321. log_filename = optarg;
  322. break;
  323. case 'I': // --input
  324. input_addr_err = !parse_io_param(&ts->input, optarg, O_RDONLY, 0);
  325. break;
  326. case '1': // --input-source
  327. if (!inet_aton(optarg, &ts->input.isrc)) {
  328. fprintf(stderr, "ERROR: Can't parse input-source IP address: %s\n", optarg);
  329. exit(EXIT_FAILURE);
  330. }
  331. break;
  332. case 'R': // --input-rtp
  333. ts->rtp_input = !ts->rtp_input;
  334. break;
  335. case 'z': // --input-ignore-disc
  336. ts->ts_discont = !ts->ts_discont;
  337. break;
  338. case 'M': // --input-service
  339. ts->forced_service_id = strtoul(optarg, NULL, 0) & 0xffff;
  340. break;
  341. case 'T': // --input-buffer
  342. ts->input_buffer_time = strtoul(optarg, NULL, 0);
  343. break;
  344. case 'W': // --input-dump
  345. ts->input_dump_filename = optarg;
  346. break;
  347. case 'O': // --output
  348. output_addr_err = !parse_io_param(&ts->output, optarg,
  349. O_CREAT | O_WRONLY | O_TRUNC,
  350. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  351. break;
  352. case 'o': // --output-intf
  353. if (strchr(optarg, '.'))
  354. inet_aton(optarg, &ts->output.intf);
  355. else
  356. ts->output.v6_if_index = atoi(optarg);
  357. break;
  358. case 't': // --output-ttl
  359. ts->output.ttl = atoi(optarg);
  360. break;
  361. case 'r': // --output-rtp
  362. ts->rtp_output = 1;
  363. break;
  364. case 'k': // --output-rtp-ssrc
  365. ts->rtp_ssrc = strtoul(optarg, NULL, 0);
  366. break;
  367. case 'g': // --output-tos
  368. ts->output.tos = (uint8_t)strtol(optarg, NULL, 0);
  369. break;
  370. case 'u': // --no-output-on-error
  371. ts->no_output_on_error = !ts->no_output_on_error;
  372. break;
  373. case 'p': // --no-output-filter
  374. ts->pid_filter = !ts->pid_filter;
  375. break;
  376. case 'y': // --output-nit-pass
  377. ts->nit_passthrough = !ts->nit_passthrough;
  378. break;
  379. case 'w': // --output-eit-pass
  380. ts->eit_passthrough = !ts->eit_passthrough;
  381. break;
  382. case 'x': // --output-tdt-pass
  383. ts->tdt_passthrough = !ts->tdt_passthrough;
  384. break;
  385. case '3': // --output-enc-pass
  386. ts->allow_encrypted_output = !ts->allow_encrypted_output;
  387. break;
  388. case 'c': // --ca-system
  389. if (strcasecmp("IRDETO", optarg) == 0)
  390. ts->req_CA_sys = CA_IRDETO;
  391. else if (strcasecmp("CONNAX", optarg) == 0 || strcasecmp("CONAX", optarg) == 0)
  392. ts->req_CA_sys = CA_CONAX;
  393. else if (strcasecmp("CRYPTOWORKS", optarg) == 0)
  394. ts->req_CA_sys = CA_CRYPTOWORKS;
  395. else if (strcasecmp("SECA", optarg) == 0 || strcasecmp("MEDIAGUARD", optarg) == 0)
  396. ts->req_CA_sys = CA_SECA;
  397. else if (strcasecmp("VIACCESS", optarg) == 0)
  398. ts->req_CA_sys = CA_VIACCESS;
  399. else if (strcasecmp("VIDEOGUARD", optarg) == 0 || strcasecmp("NDS", optarg) == 0)
  400. ts->req_CA_sys = CA_VIDEOGUARD;
  401. else if (strcasecmp("NAGRA", optarg) == 0)
  402. ts->req_CA_sys = CA_NAGRA;
  403. else if (strcasecmp("DRE-CRYPT", optarg) == 0 || strcasecmp("DRECRYPT", optarg) == 0)
  404. ts->req_CA_sys = CA_DRECRYPT;
  405. else if (strcasecmp("BULCRYPT", optarg) == 0)
  406. ts->req_CA_sys = CA_BULCRYPT;
  407. else if (strcasecmp("GRIFFIN", optarg) == 0)
  408. ts->req_CA_sys = CA_GRIFFIN;
  409. else if (strcasecmp("DGCRYPT", optarg) == 0)
  410. ts->req_CA_sys = CA_DGCRYPT;
  411. else
  412. ca_err = 1;
  413. break;
  414. case 'C': // --caid
  415. ts->forced_caid = strtoul(optarg, NULL, 0) & 0xffff;
  416. break;
  417. case 'Y': // --const-cw
  418. ts->camd.constant_codeword = 1;
  419. if (strlen(optarg) > 2 && optarg[0] == '0' && optarg[1] == 'x')
  420. optarg += 2;
  421. if (strlen(optarg) != CODEWORD_LENGTH * 2) {
  422. fprintf(stderr, "ERROR: Constant code word should be %u characters long.\n", CODEWORD_LENGTH * 2);
  423. exit(EXIT_FAILURE);
  424. }
  425. if (decode_hex_string(optarg, ts->camd.key->cw, strlen(optarg)) < 0) {
  426. fprintf(stderr, "ERROR: Invalid hex string for constant code word: %s\n", optarg);
  427. exit(EXIT_FAILURE);
  428. }
  429. camd_set_cw(ts, ts->camd.key->cw, 0);
  430. ts->camd.key->is_valid_cw = 1;
  431. break;
  432. case 'Q': // --biss-key
  433. ts->camd.constant_codeword = 1;
  434. if (strlen(optarg) > 2 && optarg[0] == '0' && optarg[1] == 'x')
  435. optarg += 2;
  436. uint8_t *key = ts->camd.key->cw;
  437. // Sometimes the BISS keys are entered with their checksums already calculated (16 symbols, 8 bytes)
  438. // This is the same as constant cw with the same key for even and odd
  439. if (strlen(optarg) == (BISSKEY_LENGTH + 2) * 2) {
  440. if (decode_hex_string(optarg, key, strlen(optarg)) < 0) {
  441. fprintf(stderr, "ERROR: Invalid hex string for BISS key: %s\n", optarg);
  442. exit(EXIT_FAILURE);
  443. }
  444. } else {
  445. // BISS key without checksum (12 symbols, 6 bytes)
  446. if (strlen(optarg) != BISSKEY_LENGTH * 2) {
  447. fprintf(stderr, "ERROR: BISS key should be %u characters long.\n", BISSKEY_LENGTH * 2);
  448. exit(EXIT_FAILURE);
  449. }
  450. if (decode_hex_string(optarg, key, strlen(optarg)) < 0) {
  451. fprintf(stderr, "ERROR: Invalid hex string for BISS key: %s\n", optarg);
  452. exit(EXIT_FAILURE);
  453. }
  454. // Calculate BISS KEY crc
  455. memmove(key + 4, key + 3, 3);
  456. key[3] = (uint8_t)(key[0] + key[1] + key[2]);
  457. key[7] = (uint8_t)(key[4] + key[5] + key[6]);
  458. }
  459. // Even and odd keys are the same
  460. memcpy(key + 8, key, 8);
  461. camd_set_cw(ts, ts->camd.key->cw, 0);
  462. ts->camd.key->is_valid_cw = 1;
  463. break;
  464. case 'A': // --camd-proto
  465. if (strcasecmp(optarg, "cs378x") == 0) {
  466. camd_proto_cs378x(&ts->camd.ops);
  467. } else if (strcasecmp(optarg, "newcamd") == 0) {
  468. camd_proto_newcamd(&ts->camd.ops);
  469. } else {
  470. fprintf(stderr, "Unknown CAMD protocol: %s\n", optarg);
  471. exit(EXIT_FAILURE);
  472. }
  473. break;
  474. case 's': // --camd-server
  475. server_err = !parse_host_and_port(optarg, &ts->camd.hostname, &ts->camd.service, &port_set);
  476. break;
  477. case 'U': // --camd-user
  478. if (strlen(optarg) < 64)
  479. ts->camd.user = optarg;
  480. break;
  481. case 'P': // --camd-pass
  482. ts->camd.pass = optarg;
  483. break;
  484. case 'B': // --camd-des-key
  485. if (strlen(optarg) > 2 && optarg[0] == '0' && optarg[1] == 'x')
  486. optarg += 2;
  487. if (strlen(optarg) != DESKEY_LENGTH) {
  488. fprintf(stderr, "ERROR: des key should be %u characters long.\n", DESKEY_LENGTH);
  489. exit(EXIT_FAILURE);
  490. }
  491. strncpy(ts->camd.newcamd.hex_des_key, optarg, sizeof(ts->camd.newcamd.hex_des_key) - 1);
  492. ts->camd.newcamd.hex_des_key[sizeof(ts->camd.newcamd.hex_des_key) - 1] = 0;
  493. break;
  494. case '4': // --ipv4
  495. ai_family = AF_INET;
  496. break;
  497. case '6': // --ipv6
  498. ai_family = AF_INET6;
  499. break;
  500. case 'e': // --emm
  501. ts->process_emm = !ts->process_emm;
  502. break;
  503. case 'Z': // --emm-pid
  504. ts->forced_emm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  505. break;
  506. case 'E': // --emm-only
  507. ts->process_emm = 1;
  508. ts->process_ecm = 0;
  509. ts->output_stream = 0;
  510. break;
  511. case 'f': // --emm-report-time
  512. ts->emm_report_interval = strtoul(optarg, NULL, 10);
  513. if (ts->emm_report_interval > 86400)
  514. ts->emm_report_interval = 86400;
  515. break;
  516. case 'a': // --emm-filter
  517. if (ts->emm_filters_num + 1 > MAX_FILTERS) {
  518. fprintf(stderr, "ERROR: Maximum allowed filters are %d.\n", MAX_FILTERS);
  519. exit(EXIT_FAILURE);
  520. }
  521. if (filter_parse(optarg, &ts->emm_filters[ts->emm_filters_num])) {
  522. ts->emm_filters_num++;
  523. } else {
  524. fprintf(stderr, "ERROR: Can't parse EMM filter: %s\n", optarg);
  525. exit(EXIT_FAILURE);
  526. }
  527. break;
  528. case 'X': // --ecm-pid
  529. ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  530. break;
  531. case 'v': // --ecm-only
  532. ts->process_emm = 0;
  533. ts->process_ecm = 1;
  534. ts->output_stream = 0;
  535. break;
  536. case 'H': // --ecm-report-time
  537. ts->ecm_report_interval = strtoul(optarg, NULL, 10);
  538. if (ts->ecm_report_interval > 86400)
  539. ts->ecm_report_interval = 86400;
  540. break;
  541. case 'G': // --ecm-irdeto-type
  542. ts->irdeto_ecm_idx = strtoul(optarg, NULL, 0);
  543. ts->irdeto_ecm_filter_type = IRDETO_FILTER_IDX;
  544. break;
  545. case '2': // --ecm-irdeto-chid
  546. ts->irdeto_ecm_chid = strtoul(optarg, NULL, 0);
  547. ts->irdeto_ecm_filter_type = IRDETO_FILTER_CHID;
  548. break;
  549. case 'K': // --ecm-no-log
  550. ts->ecm_cw_log = !ts->ecm_cw_log;
  551. break;
  552. case 'J': // --cw-warn-time
  553. ts->cw_warn_sec = strtoul(optarg, NULL, 10);
  554. if (ts->cw_warn_sec > 86400)
  555. ts->cw_warn_sec = 86400;
  556. ts->cw_last_warn= ts->cw_last_warn + ts->cw_warn_sec;
  557. break;
  558. case 'q': // --ecm-and-emm-only
  559. ts->process_emm = 1;
  560. ts->process_ecm = 1;
  561. ts->output_stream = 0;
  562. break;
  563. case 'D': // --debug
  564. ts->debug_level = atoi(optarg);
  565. if (ts->debug_level > 0)
  566. ts->pid_report = 1;
  567. break;
  568. case 'j': // --pid-report
  569. ts->pid_report = 1;
  570. break;
  571. case 'b': // --bench
  572. csa_benchmark();
  573. exit(EXIT_SUCCESS);
  574. case 'n': // --ecm-file
  575. case 'm': // --emm-file
  576. packet_from_file = 1;
  577. packet_buflen = file_hex2buf(optarg, packet_buf, sizeof(packet_buf));
  578. if (!packet_buflen) {
  579. fprintf(stderr, "ERROR: Can't init packet from file.\n");
  580. exit(1);
  581. }
  582. packet_type = j == 'n' ? ECM_MSG : EMM_MSG;
  583. break;
  584. case 'h': // --help
  585. show_help(ts);
  586. exit(EXIT_SUCCESS);
  587. case 'V': // --version
  588. printf("%s\n", program_id);
  589. exit(EXIT_SUCCESS);
  590. }
  591. }
  592. if (!ts->ident) {
  593. if (ts->syslog_active || ts->notify_program || ts->status_file)
  594. ident_err = 1;
  595. }
  596. if (packet_from_file) {
  597. int err = 0;
  598. if (!ts->forced_caid) {
  599. fprintf(stderr, "ERROR: CAID was not set. Use --caid option.\n");
  600. err++;
  601. }
  602. if (!ts->forced_service_id) {
  603. fprintf(stderr, "ERROR: Service id was not set. Use --input-service option.\n");
  604. err++;
  605. }
  606. if (err)
  607. exit(EXIT_FAILURE);
  608. ts->threaded = 0;
  609. input_addr_err = 0;
  610. output_addr_err = 0;
  611. ts->input.type = FILE_IO;
  612. ts->input.fd = 0;
  613. ts->output.type = FILE_IO;
  614. ts->output.fd = 1;
  615. ts->pid_filter = 0;
  616. ts->process_ecm = 0;
  617. ts->process_emm = 0;
  618. ts->output_stream = 0;
  619. ts->camd.no_reconnect = 1;
  620. ts->camd.check_emm_errors = 1;
  621. ts->emm_filters_num = 0;
  622. }
  623. // Constant codeword is special. Disable conflicting options
  624. if (ts->camd.constant_codeword) {
  625. server_err = 0; // No server settings are required
  626. ts->process_ecm = 0;
  627. ts->process_emm = 0;
  628. ts->output_stream = 1;
  629. }
  630. if (ident_err || ca_err || server_err || input_addr_err || output_addr_err || ts->input.type == WTF_IO || ts->output.type == WTF_IO) {
  631. if (ident_err)
  632. fprintf(stderr, "ERROR: Ident is not set, please use --ident option.\n");
  633. if (ca_err)
  634. fprintf(stderr, "ERROR: Requested CA system is unsupported.\n");
  635. if (server_err)
  636. fprintf(stderr, "ERROR: CAMD server address is not set or it is invalid.\n");
  637. if (input_addr_err)
  638. fprintf(stderr, "ERROR: Input address is invalid.\n");
  639. if (output_addr_err)
  640. fprintf(stderr, "ERROR: Output address is invalid.\n");
  641. exit(EXIT_FAILURE);
  642. }
  643. if (decode_hex_string(ts->camd.newcamd.hex_des_key, ts->camd.newcamd.bin_des_key, DESKEY_LENGTH) < 0) {
  644. fprintf(stderr, "ERROR: Invalid hex string for des key: %s\n", ts->camd.newcamd.hex_des_key);
  645. exit(EXIT_FAILURE);
  646. }
  647. if (ts->camd.ops.proto == CAMD_NEWCAMD && !port_set) {
  648. fprintf(stderr, "ERROR: CAMD server port is not set. Use --camd-server %s:xxxx to set the port.\n", ts->camd.hostname);
  649. exit(EXIT_FAILURE);
  650. }
  651. if (log_filename) {
  652. log_file = fopen(log_filename, "a");
  653. if (!log_file) {
  654. fprintf(stderr, "ERROR: Can't open log file %s: %s\n", log_filename, strerror(errno));
  655. exit(EXIT_FAILURE);
  656. }
  657. }
  658. if (ts->ident)
  659. ts_LOGf("Ident : %s\n", ts->ident);
  660. if (ts->notify_program)
  661. ts_LOGf("Notify prg : %s (%s)\n", ts->notify_program, ts->notify_wait ? "sync" : "async");
  662. if (ts->status_file)
  663. ts_LOGf("Status file: %s\n", ts->status_file);
  664. if (ts->pidfile)
  665. ts_LOGf("Daemonize : %s pid file.\n", ts->pidfile);
  666. if (ts->syslog_active) {
  667. if (ts->syslog_remote)
  668. ts_LOGf("Syslog : %s:%d\n", ts->syslog_host, ts->syslog_port);
  669. else
  670. ts_LOGf("Syslog : enabled\n");
  671. } else {
  672. if (!packet_from_file)
  673. ts_LOGf("Syslog : disabled\n");
  674. }
  675. if (!ts->camd.constant_codeword) {
  676. if (ts->forced_caid)
  677. ts->req_CA_sys = ts_get_CA_sys(ts->forced_caid);
  678. if (!ts->forced_caid)
  679. ts_LOGf("CA System : %s\n", ts_get_CA_sys_txt(ts->req_CA_sys));
  680. else
  681. ts_LOGf("CA System : %s | CAID: 0x%04x (%d)\n",
  682. ts_get_CA_sys_txt(ts->req_CA_sys),
  683. ts->forced_caid, ts->forced_caid);
  684. } else {
  685. char cw_even[64], cw_odd[64];
  686. ts_hex_dump_buf(cw_even, sizeof(cw_even), ts->key.cw , 8, 0);
  687. ts_hex_dump_buf(cw_odd , sizeof(cw_odd ), ts->key.cw + 8, 8, 0);
  688. ts_LOGf("Constant CW: even = %s\n", cw_even);
  689. ts_LOGf("Constant CW: odd = %s\n", cw_odd);
  690. }
  691. if (ts->input.type == NET_IO) {
  692. ts_LOGf("Input addr : %s://%s:%s/\n",
  693. ts->rtp_input ? "rtp" : "udp",
  694. ts->input.hostname, ts->input.service);
  695. ts_LOGf("Input src : %s\n", inet_ntoa(ts->input.isrc));
  696. if (ts->input_buffer_time) {
  697. ts_LOGf("Input buff : %u ms\n", ts->input_buffer_time);
  698. }
  699. } else if (ts->input.type == FILE_IO) {
  700. if (!packet_from_file)
  701. ts_LOGf("Input file : %s\n", ts->input.fd == 0 ? "STDIN" : ts->input.fname);
  702. }
  703. if (ts->input_dump_filename) {
  704. ts->input_dump_file = fopen(ts->input_dump_filename, "w");
  705. if (ts->input_dump_file)
  706. ts_LOGf("Input dump : %s\n", ts->input_dump_filename);
  707. else
  708. ts_LOGf("Input dump : %s | ERROR: %s\n", ts->input_dump_filename, strerror(errno));
  709. }
  710. if (ts->forced_service_id)
  711. ts_LOGf("Service id : 0x%04x (%d)\n",
  712. ts->forced_service_id, ts->forced_service_id);
  713. if (ts->req_CA_sys == CA_IRDETO) {
  714. switch (ts->irdeto_ecm_filter_type) {
  715. case IRDETO_FILTER_IDX : ts_LOGf("Irdeto ECM : Index: 0x%02x (%d)\n", ts->irdeto_ecm_idx, ts->irdeto_ecm_idx); break;
  716. case IRDETO_FILTER_CHID: ts_LOGf("Irdeto ECM : CHID: 0x%04x (%d)\n", ts->irdeto_ecm_chid, ts->irdeto_ecm_chid); break;
  717. }
  718. }
  719. if (ts->output_stream) {
  720. if (ts->output.type == NET_IO) {
  721. ts_LOGf("Output addr: %s://%s:%s/\n",
  722. ts->rtp_output ? "rtp" : "udp",
  723. ts->output.hostname, ts->output.service);
  724. ts_LOGf("Output intf: %s (IPv6 intf index:%d)\n",
  725. inet_ntoa(ts->output.intf), ts->output.v6_if_index);
  726. ts_LOGf("Output ttl : %d\n", ts->output.ttl);
  727. if (ts->output.tos > -1)
  728. ts_LOGf("Output TOS : %u (0x%02x)\n", ts->output.tos, ts->output.tos);
  729. if (ts->rtp_output) {
  730. ts_LOGf("RTP SSRC : %u (0x%04x)\n",
  731. ts->rtp_ssrc, ts->rtp_ssrc);
  732. // It is recommended that RTP seqnum starts with random number
  733. RAND_bytes((unsigned char *)&(ts->rtp_seqnum), 2);
  734. }
  735. } else if (ts->output.type == FILE_IO) {
  736. ts_LOGf("Output file: %s\n", ts->output.fd == 1 ? "STDOUT" : ts->output.fname);
  737. }
  738. ts_LOGf("Out filter : %s (%s)%s\n",
  739. ts->pid_filter ? "enabled" : "disabled",
  740. ts->pid_filter ? "output only service related PIDs" : "output everything",
  741. ts->no_output_on_error ? " (No output on CW error)" : ""
  742. );
  743. if (ts->pid_filter) {
  744. if (ts->nit_passthrough)
  745. ts_LOGf("Out filter : Pass through NIT.\n");
  746. if (ts->eit_passthrough)
  747. ts_LOGf("Out filter : Pass through EIT (EPG).\n");
  748. if (ts->tdt_passthrough)
  749. ts_LOGf("Out filter : Pass through TDT/TOT.\n");
  750. }
  751. ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
  752. ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
  753. ts_LOGf("Decoding : %s\n", ts->threaded ? "threaded" : "single thread");
  754. } else {
  755. ts_LOGf("Decoding : disabled\n");
  756. }
  757. if (!ts->camd.constant_codeword) {
  758. ts_LOGf("CAMD proto : %s\n", ts->camd.ops.ident);
  759. ts_LOGf("CAMD addr : %s:%s%s\n", ts->camd.hostname, ts->camd.service,
  760. ai_family == AF_INET ? " (IPv4 only)" :
  761. ai_family == AF_INET6 ? " (IPv6 only)" :
  762. " (IPv4/IPv6)"
  763. );
  764. ts_LOGf("CAMD user : %s\n", ts->camd.user);
  765. ts_LOGf("CAMD pass : %s\n", ts->camd.pass);
  766. if (ts->camd.ops.proto == CAMD_NEWCAMD)
  767. ts_LOGf("CAMD deskey: %s\n", ts->camd.newcamd.hex_des_key);
  768. }
  769. if (!packet_from_file)
  770. ts_LOGf("EMM process: %s\n", ts->process_emm ? "Yes" : "No");
  771. if (ts->process_emm) {
  772. if (ts->forced_emm_pid)
  773. ts_LOGf("EMM pid : 0x%04x (%d)\n", ts->forced_emm_pid, ts->forced_emm_pid);
  774. if (ts->emm_report_interval)
  775. ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
  776. else
  777. ts_LOGf("EMM report : disabled\n");
  778. for (i = 0; i < ts->emm_filters_num; i++) {
  779. char tmp[512];
  780. filter_dump(&ts->emm_filters[i], tmp, sizeof(tmp));
  781. ts_LOGf("EMM filter : [%2d] %s\n", i + 1, tmp);
  782. }
  783. }
  784. if (!packet_from_file)
  785. ts_LOGf("ECM process: %s\n", ts->process_ecm ? "Yes" : "No");
  786. if (ts->process_ecm) {
  787. if (ts->forced_ecm_pid)
  788. ts_LOGf("ECM pid : 0x%04x (%d)\n", ts->forced_ecm_pid, ts->forced_ecm_pid);
  789. if (ts->ecm_report_interval)
  790. ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);
  791. else
  792. ts_LOGf("ECM report : disabled\n");
  793. if (ts->cw_warn_sec)
  794. ts_LOGf("CW warning : %d sec\n", ts->cw_warn_sec);
  795. else
  796. ts_LOGf("CW warning : disabled\n");
  797. if (!ts->ecm_cw_log)
  798. ts_LOGf("ECM/CW log : disabled\n");
  799. }
  800. if (ts->ident) {
  801. int len = strlen(ts->ident);
  802. for (i = 0; i < len; i++) {
  803. if (ts->ident[i] == '/')
  804. ts->ident[i] = '-';
  805. }
  806. }
  807. }
  808. static void report_emms(struct ts *ts, time_t now) {
  809. ts_LOGf("EMM | Received %u, Skipped %u, Sent %u, Processed %u in %lu seconds.\n",
  810. ts->emm_input_count,
  811. ts->emm_skipped_count,
  812. ts->emm_seen_count,
  813. ts->emm_processed_count,
  814. now - ts->emm_last_report);
  815. if (ts->emm_seen_count == 0) {
  816. notify(ts, "NO_EMM_RECEIVED", "No EMMs were received in last %lu seconds.",
  817. now - ts->emm_last_report);
  818. }
  819. ts->emm_last_report = now;
  820. ts->emm_input_count = 0;
  821. ts->emm_seen_count = 0;
  822. ts->emm_skipped_count = 0;
  823. ts->emm_processed_count = 0;
  824. }
  825. static void report_ecms(struct ts *ts, time_t now) {
  826. if ((ts->stream_is_not_scrambled || !ts->have_valid_pmt || ts->no_input) && ts->ecm_seen_count == 0)
  827. return;
  828. ts_LOGf("ECM | Received %u (%u dup) and processed %u in %lu seconds.\n",
  829. ts->ecm_seen_count,
  830. ts->ecm_duplicate_count,
  831. ts->ecm_processed_count,
  832. now - ts->ecm_last_report);
  833. ts->ecm_last_report = now;
  834. ts->ecm_seen_count = 0;
  835. ts->ecm_duplicate_count = 0;
  836. ts->ecm_processed_count = 0;
  837. }
  838. static void report_cw_warn(struct ts *ts, time_t now) {
  839. if (ts->stream_is_not_scrambled || !ts->have_valid_pmt || ts->no_input)
  840. return;
  841. if (now - ts->key.ts > 1) {
  842. notify(ts, "NO_CODE_WORD", "No valid code word was received in %ld sec.",
  843. now - ts->key.ts);
  844. ts_LOGf("CW | *ERR* No valid code word was received for %ld seconds!\n",
  845. now - ts->key.ts);
  846. }
  847. ts->cw_last_warn = now;
  848. ts->cw_next_warn = now + ts->cw_warn_sec;
  849. }
  850. static void do_reports(struct ts *ts) {
  851. static int first_emm_report = 1;
  852. static int first_ecm_report = 1;
  853. time_t now = time(NULL);
  854. if (ts->process_emm && ts->emm_report_interval) {
  855. if (first_emm_report && now >= ts->emm_last_report) {
  856. first_emm_report = 0;
  857. ts->emm_last_report -= FIRST_REPORT_SEC;
  858. report_emms(ts, now);
  859. } else if ((time_t)(ts->emm_last_report + ts->emm_report_interval) <= now) {
  860. report_emms(ts, now);
  861. }
  862. }
  863. if (ts->process_ecm && ts->ecm_report_interval) {
  864. if (first_ecm_report && now >= ts->ecm_last_report) {
  865. first_ecm_report = 0;
  866. ts->ecm_last_report -= FIRST_REPORT_SEC;
  867. report_ecms(ts, now);
  868. } else if ((time_t)(ts->ecm_last_report + ts->ecm_report_interval) <= now) {
  869. report_ecms(ts, now);
  870. }
  871. }
  872. if (ts->stream_is_not_scrambled && ts->last_not_scrambled_report_ts <= now - 60) {
  873. ts_LOGf("CLR | No encrypted packets in the last %ld seconds. Stream is clear.\n", now - ts->last_scrambled_packet_ts);
  874. notify(ts, "STREAM_CLEAR", "No encrypted packets in the last %ld seconds. Stream is clear.", now - ts->last_scrambled_packet_ts);
  875. ts->last_not_scrambled_report_ts = now;
  876. ts->key.is_valid_cw = 0;
  877. } else {
  878. if (ts->process_ecm && !ts->key.is_valid_cw) {
  879. if (ts->cw_warn_sec && now >= ts->cw_next_warn) {
  880. report_cw_warn(ts, now);
  881. }
  882. }
  883. }
  884. if (!ts->no_input) {
  885. if (ts->last_pmt_ts <= now - 3) {
  886. if (ts->have_valid_pmt) {
  887. ts_LOGf("MIS | There is no valid PMT in the input.\n");
  888. notify(ts, "NO_PROGRAM", "The input is missing valid program.");
  889. ts->have_valid_pmt = 0;
  890. ts->key.is_valid_cw = 0;
  891. }
  892. }
  893. }
  894. }
  895. static struct ts ts;
  896. void signal_quit(int sig) {
  897. if (!keep_running)
  898. raise(sig);
  899. keep_running = 0;
  900. ts_LOGf("Killed %s with signal %d\n", program_id, sig);
  901. if (ts.input.type == NET_IO)
  902. shutdown_fd(&ts.input.fd);
  903. if (ts.output.type == NET_IO)
  904. shutdown_fd(&ts.output.fd);
  905. signal(sig, SIG_DFL);
  906. }
  907. void signal_alarm(int sig) {
  908. (void)sig;
  909. raise(SIGINT);
  910. }
  911. #define RTP_HDR_SZ 12
  912. static uint8_t ts_packet[FRAME_SIZE + RTP_HDR_SZ];
  913. static uint8_t rtp_hdr[2][RTP_HDR_SZ];
  914. int main(int argc, char **argv) {
  915. ssize_t readen;
  916. int have_data = 1;
  917. int ntimeouts = 0;
  918. time_t timeout_start = time(NULL);
  919. int rtp_hdr_pos = 0, num_packets = 0;
  920. struct rlimit rl;
  921. if (getrlimit(RLIMIT_STACK, &rl) == 0) {
  922. if (rl.rlim_cur > THREAD_STACK_SIZE) {
  923. rl.rlim_cur = THREAD_STACK_SIZE;
  924. setrlimit(RLIMIT_STACK, &rl);
  925. }
  926. }
  927. memset(rtp_hdr[0], 0, RTP_HDR_SZ);
  928. memset(rtp_hdr[1], 0, RTP_HDR_SZ);
  929. data_init(&ts);
  930. ts_set_log_func(LOG_func);
  931. parse_options(&ts, argc, argv);
  932. if (ts.pidfile)
  933. daemonize(ts.pidfile);
  934. if (ts.syslog_active) {
  935. if (ts.syslog_remote) {
  936. log_init(ts.ident, 1, 1, ts.syslog_host, ts.syslog_port);
  937. remote_syslog = 1;
  938. } else {
  939. openlog(ts.ident, LOG_NDELAY | LOG_PID, LOG_USER);
  940. local_syslog = 1;
  941. }
  942. }
  943. ts.notify = notify_alloc(&ts);
  944. ts_LOGf("Start %s\n", program_id);
  945. notify(&ts, "START", "Starting %s", program_id);
  946. if (ts.input.type == NET_IO && udp_connect_input(&ts.input) < 1)
  947. goto EXIT;
  948. if (ts.output.type == NET_IO && udp_connect_output(&ts.output) < 1)
  949. goto EXIT;
  950. signal(SIGCHLD, SIG_IGN);
  951. signal(SIGPIPE, SIG_IGN);
  952. signal(SIGINT , signal_quit);
  953. signal(SIGTERM, signal_quit);
  954. if (ts.threaded) {
  955. pthread_create(&ts.decode_thread, &ts.thread_attr, &decode_thread, &ts);
  956. pthread_create(&ts.write_thread , &ts.thread_attr, &write_thread , &ts);
  957. }
  958. ts.emm_last_report = time(NULL) + FIRST_REPORT_SEC;
  959. ts.ecm_last_report = time(NULL) + FIRST_REPORT_SEC;
  960. camd_start(&ts);
  961. if (packet_from_file) {
  962. uint8_t tmp[2048];
  963. ts_hex_dump_buf((char *)tmp, sizeof(tmp), packet_buf, packet_buflen, 16);
  964. ts_LOGf("%s | Processing packet with CAID 0x%04x\n", packet_type == ECM_MSG ? "ECM" : "EMM", ts.forced_caid);
  965. ts_LOGf("%s | Packet dump:\n%s\n", packet_type == ECM_MSG ? "ECM" : "EMM", tmp);
  966. camd_process_packet(&ts, camd_msg_alloc(packet_type, ts.forced_caid, ts.forced_service_id, packet_buf, packet_buflen));
  967. goto EXIT;
  968. }
  969. do {
  970. if (!ts.camd.constant_codeword)
  971. do_reports(&ts);
  972. if (ts.input.type == NET_IO) {
  973. set_log_io_errors(0);
  974. if (!ts.rtp_input) {
  975. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE, 250, 4, 1);
  976. } else {
  977. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE + RTP_HDR_SZ, 250, 4, 1);
  978. if (readen > RTP_HDR_SZ) {
  979. memcpy(rtp_hdr[rtp_hdr_pos], ts_packet, RTP_HDR_SZ);
  980. memmove(ts_packet, ts_packet + RTP_HDR_SZ, FRAME_SIZE);
  981. readen -= RTP_HDR_SZ;
  982. uint16_t ssrc = (rtp_hdr[rtp_hdr_pos][2] << 8) | rtp_hdr[rtp_hdr_pos][3];
  983. uint16_t pssrc = (rtp_hdr[!rtp_hdr_pos][2] << 8) | rtp_hdr[!rtp_hdr_pos][3];
  984. rtp_hdr_pos = !rtp_hdr_pos;
  985. if (pssrc + 1 != ssrc && (ssrc != 0 && pssrc != 0xffff) && num_packets > 2)
  986. if (ts.ts_discont)
  987. ts_LOGf("--- | RTP discontinuity last_ssrc %5d, curr_ssrc %5d, lost %d packet\n",
  988. pssrc, ssrc, ((ssrc - pssrc)-1) & 0xffff);
  989. num_packets++;
  990. }
  991. }
  992. set_log_io_errors(1);
  993. if (!keep_running)
  994. break;
  995. if (readen < 0) {
  996. ts_LOGf("--- | Input read timeout.\n");
  997. if (!ntimeouts) {
  998. timeout_start = time(NULL);
  999. notify(&ts, "INPUT_TIMEOUT", "Read timeout on input %s://%s:%s/",
  1000. ts.rtp_input ? "rtp" : "udp",
  1001. ts.input.hostname, ts.input.service);
  1002. }
  1003. ts.no_input = 1;
  1004. ntimeouts++;
  1005. } else {
  1006. if (ntimeouts && readen > 0) {
  1007. time_t now = time(NULL);
  1008. ts_LOGf("+++ | Input OK after %ld sec timeout.\n", (now - timeout_start) + 2);
  1009. notify(&ts, "INPUT_OK", "Data is available on input %s://%s:%s/ after %ld seconds timeout.",
  1010. ts.rtp_input ? "rtp" : "udp",
  1011. ts.input.hostname, ts.input.service,
  1012. (now - timeout_start) + 2); // Timeout is detected when ~2 seconds there is no incoming data
  1013. ts.have_valid_pmt = 1; // We need to report if there is not PMT after INPUT is now OK
  1014. ts.last_pmt_ts = time(NULL);
  1015. ts.no_input = 0;
  1016. ntimeouts = 0;
  1017. }
  1018. }
  1019. } else {
  1020. readen = read(ts.input.fd, ts_packet, FRAME_SIZE);
  1021. have_data = !(readen <= 0);
  1022. }
  1023. if (readen > 0) {
  1024. if (ts.input_dump_file)
  1025. fwrite(ts_packet, readen, 1, ts.input_dump_file);
  1026. process_packets(&ts, ts_packet, readen);
  1027. }
  1028. } while (have_data && keep_running);
  1029. EXIT:
  1030. camd_stop(&ts);
  1031. // If pthread_join failes make sure we exit...
  1032. signal(SIGINT , SIG_DFL);
  1033. signal(SIGTERM, SIG_DFL);
  1034. signal(SIGALRM, signal_alarm);
  1035. alarm(2);
  1036. if (ts.threaded) {
  1037. ts.decode_stop = 1;
  1038. ts.write_stop = 1;
  1039. if (ts.decode_thread)
  1040. pthread_join(ts.decode_thread, NULL);
  1041. if (ts.write_thread)
  1042. pthread_join(ts.write_thread, NULL);
  1043. }
  1044. show_pid_report(&ts);
  1045. notify_sync(&ts, "STOP", "Stopping %s", program_id);
  1046. ts_LOGf("Stop %s\n", program_id);
  1047. if (ts.syslog_active) {
  1048. if (ts.syslog_remote)
  1049. log_close();
  1050. else
  1051. closelog();
  1052. }
  1053. if (ts.input_dump_file)
  1054. fclose(ts.input_dump_file);
  1055. if (ts.pidfile)
  1056. unlink(ts.pidfile);
  1057. if (log_file)
  1058. fclose(log_file);
  1059. notify_free(&ts.notify);
  1060. data_free(&ts);
  1061. exit(EXIT_SUCCESS);
  1062. }