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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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 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 <getopt.h>
  21. #include <string.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <signal.h>
  25. #include <fcntl.h>
  26. #include <errno.h>
  27. #include <syslog.h>
  28. #include <sys/resource.h>
  29. #include <dvbcsa/dvbcsa.h>
  30. #include <openssl/rand.h>
  31. #include "libfuncs/libfuncs.h"
  32. #include "data.h"
  33. #include "util.h"
  34. #include "camd.h"
  35. #include "process.h"
  36. #include "udp.h"
  37. #include "notify.h"
  38. #define FIRST_REPORT_SEC 3
  39. #define PROGRAM_NAME "tsdecrypt"
  40. static const char *program_id = PROGRAM_NAME " v" VERSION " (" GIT_VER ", build " BUILD_ID ")";
  41. static int keep_running = 1;
  42. static FILE *log_file = NULL;
  43. static char *log_filename = NULL;
  44. static int local_syslog = 0;
  45. static int remote_syslog = 0;
  46. static void do_log(FILE *f, time_t now, const char *msg) {
  47. char date[64];
  48. struct tm tm;
  49. localtime_r(&now, &tm);
  50. strftime(date, sizeof(date), "%F %H:%M:%S", localtime_r(&now, &tm));
  51. fprintf(f, "%s | %s", date, msg);
  52. }
  53. static void LOG_func(const char *msg) {
  54. time_t now = time(NULL);
  55. do_log(stderr, now, msg);
  56. if (log_file)
  57. do_log(log_file, now, msg);
  58. if (local_syslog)
  59. syslog(LOG_INFO, msg, strlen(msg));
  60. if (remote_syslog)
  61. LOG(msg);
  62. }
  63. /* The following routine is taken from benchbitslice in libdvbcsa */
  64. void run_benchmark(void) {
  65. struct timeval t0, t1;
  66. struct dvbcsa_bs_key_s *ffkey = dvbcsa_bs_key_alloc();
  67. unsigned int n, i, c = 0, pkt_len = 0;
  68. unsigned int gs = dvbcsa_bs_batch_size();
  69. uint8_t data[gs + 1][184];
  70. struct dvbcsa_bs_batch_s pcks[gs + 1];
  71. uint8_t cw[8] = { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, };
  72. srand(time(0));
  73. puts("* Single threaded libdvbcsa benchmark *");
  74. dvbcsa_bs_key_set (cw, ffkey);
  75. printf(" - Generating batch with %i randomly sized packets\n\n", gs);
  76. for (i = 0; i < gs; i++) {
  77. pcks[i].data = data[i];
  78. pcks[i].len = 100 + rand() % 85;
  79. memset(data[i], rand(), pcks[i].len);
  80. pkt_len += pcks[i].len;
  81. }
  82. pcks[i].data = NULL;
  83. gettimeofday(&t0, NULL);
  84. for (n = (1 << 12) / gs; n < (1 << 19) / gs; n *= 2) {
  85. printf(" - Decrypting %u TS packets\n", n * gs);
  86. for (i = 0; i < n; i++) {
  87. dvbcsa_bs_decrypt(ffkey, pcks, 184);
  88. }
  89. c += n * gs;
  90. }
  91. gettimeofday(&t1, NULL);
  92. printf("\n* %u packets proceded: %.1f Mbits/s\n\n", c,
  93. (float)(c * 188 * 8) / (float)timeval_diff_usec(&t0, &t1)
  94. /*(float)((t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec)) */
  95. );
  96. dvbcsa_bs_key_free(ffkey);
  97. puts("* Done *");
  98. }
  99. static const char short_options[] = "i:d:N:Sl:L:F:I:RzM:T:W:O:o:t:rk:g:pwxyc:C:Y:A:s:U:P:B:eZ:Ef:X:H:G:KJ:D:jbhV";
  100. // Unused short options: Qamnquv0123456789
  101. static const struct option long_options[] = {
  102. { "ident", required_argument, NULL, 'i' },
  103. { "daemon", required_argument, NULL, 'd' },
  104. { "syslog", no_argument, NULL, 'S' },
  105. { "syslog-host", required_argument, NULL, 'l' },
  106. { "syslog-port", required_argument, NULL, 'L' },
  107. { "log-file", required_argument, NULL, 'F' },
  108. { "notify-program", required_argument, NULL, 'N' },
  109. { "input", required_argument, NULL, 'I' },
  110. { "input-rtp", no_argument, NULL, 'R' },
  111. { "input-ignore-disc", no_argument, NULL, 'z' },
  112. { "input-service", required_argument, NULL, 'M' },
  113. { "input-buffer", required_argument, NULL, 'T' },
  114. { "input-dump", required_argument, NULL, 'W' },
  115. { "output", required_argument, NULL, 'O' },
  116. { "output-intf", required_argument, NULL, 'o' },
  117. { "output-ttl", required_argument, NULL, 't' },
  118. { "output-rtp", no_argument, NULL, 'r' },
  119. { "output-rtp-ssrc", required_argument, NULL, 'k' },
  120. { "output-tos", required_argument, NULL, 'g' },
  121. { "output-filter", no_argument, NULL, 'p' },
  122. { "no-output-filter", no_argument, NULL, 'p' },
  123. { "output-nit-pass", no_argument, NULL, 'y' },
  124. { "output-eit-pass", no_argument, NULL, 'w' },
  125. { "output-tdt-pass", no_argument, NULL, 'x' },
  126. { "ca-system", required_argument, NULL, 'c' },
  127. { "caid", required_argument, NULL, 'C' },
  128. { "const-cw", required_argument, NULL, 'Y' },
  129. { "camd-proto", required_argument, NULL, 'A' },
  130. { "camd-server", required_argument, NULL, 's' },
  131. { "camd-user", required_argument, NULL, 'U' },
  132. { "camd-pass", required_argument, NULL, 'P' },
  133. { "camd-des-key", required_argument, NULL, 'B' },
  134. { "emm", no_argument, NULL, 'e' },
  135. { "emm-pid", required_argument, NULL, 'Z' },
  136. { "emm-only", no_argument, NULL, 'E' },
  137. { "emm-report-time", required_argument, NULL, 'f' },
  138. { "ecm-pid", required_argument, NULL, 'X' },
  139. { "ecm-report-time", required_argument, NULL, 'H' },
  140. { "ecm-irdeto-type", required_argument, NULL, 'G' },
  141. { "ecm-no-log", no_argument , NULL, 'K' },
  142. { "cw-warn-time", required_argument, NULL, 'J' },
  143. { "debug", required_argument, NULL, 'D' },
  144. { "pid-report", no_argument, NULL, 'j' },
  145. { "bench", no_argument, NULL, 'b' },
  146. { "help", no_argument, NULL, 'h' },
  147. { "version", no_argument, NULL, 'V' },
  148. { 0, 0, 0, 0 }
  149. };
  150. static void show_help(struct ts *ts) {
  151. printf("%s\n", program_id);
  152. printf("Copyright (C) 2011-2012 Unix Solutions Ltd.\n");
  153. printf("\n");
  154. printf(" Usage: " PROGRAM_NAME " [opts]\n");
  155. printf("\n");
  156. printf("Main options:\n");
  157. printf(" -i --ident <server> | Format PROVIDER/CHANNEL. Default: empty\n");
  158. printf(" -d --daemon <pidfile> | Daemonize program and write pid file.\n");
  159. printf(" -N --notify-program <prg> | Execute <prg> to report events. Default: empty\n");
  160. printf("\n");
  161. printf("Input options:\n");
  162. printf(" -I --input <source> | Where to read from. File or multicast address.\n");
  163. printf(" . -I 224.0.0.1:5000 (multicast receive)\n");
  164. printf(" . -I file.ts (read from file)\n");
  165. printf(" . -I - (read from stdin) (default)\n");
  166. printf(" -R --input-rtp | Enable RTP input\n");
  167. printf(" -z --input-ignore-disc | Do not report discontinuty errors in input.\n");
  168. printf(" -M --input-service <srvid> | Choose service id when input is MPTS.\n");
  169. printf(" -T --input-buffer <ms> | Set input buffer time in ms. Default: %u\n", ts->input_buffer_time);
  170. printf(" -W --input-dump <filename> | Save input stream in file.\n");
  171. printf("\n");
  172. printf("Output options:\n");
  173. printf(" -O --output <dest> | Where to send output. File or multicast address.\n");
  174. printf(" . -O 239.0.0.1:5000 (multicast send)\n");
  175. printf(" . -O file.ts (write to file)\n");
  176. printf(" . -O - (write to stdout) (default)\n");
  177. printf(" -o --output-intf <addr> | Set multicast output interface. Default: %s\n", inet_ntoa(ts->output.intf));
  178. printf(" -t --output-ttl <ttl> | Set multicast ttl. Default: %d\n", ts->output.ttl);
  179. printf(" -r --output-rtp | Enable RTP output.\n");
  180. printf(" -k --output-rtp-ssrc <id> | Set RTP SSRC. Default: %u\n", ts->rtp_ssrc);
  181. printf(" -g --output-tos <tos> | Set TOS value of output packets. Default: none\n");
  182. printf(" -p --no-output-filter | Disable output filtering. Default: %s\n", ts->pid_filter ? "enabled" : "disabled");
  183. printf(" -y --output-nit-pass | Pass through NIT.\n");
  184. printf(" -w --output-eit-pass | Pass through EIT (EPG).\n");
  185. printf(" -x --output-tdt-pass | Pass through TDT/TOT.\n");
  186. printf("\n");
  187. printf("CA options:\n");
  188. printf(" -c --ca-system <ca_sys> | Process input EMM/ECM from <ca_sys>.\n");
  189. printf(" | Valid systems are: CONAX (default), CRYPTOWORKS,\n");
  190. printf(" . IRDETO, SECA (MEDIAGUARD), VIACCESS,\n");
  191. printf(" . VIDEOGUARD (NDS), NAGRA and DRECRYPT.\n");
  192. printf(" -C --caid <caid> | Set CAID. Default: Taken from --ca-system.\n");
  193. printf(" -Y --const-cw <codeword> | Set constant code word for decryption.\n");
  194. printf(" . Example cw: a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8\n");
  195. printf("\n");
  196. printf("CAMD server options:\n");
  197. printf(" -A --camd-proto <proto> | Set CAMD network protocol.\n");
  198. printf(" . Valid protocols are: CS378X (default) and NEWCAMD\n");
  199. printf(" -s --camd-server <addr> | Set CAMD server ip_address:port (1.2.3.4:2233).\n");
  200. printf(" -U --camd-user <user> | Set CAMD server user. Default: %s\n", ts->camd.user);
  201. printf(" -P --camd-pass <pass> | Set CAMD server password. Default: %s\n", ts->camd.pass);
  202. printf(" -B --camd-des-key <key> | Set DES key for newcamd protocol.\n");
  203. printf(" . Default: %s\n", ts->camd.newcamd.hex_des_key);
  204. printf("\n");
  205. printf("EMM options:\n");
  206. printf(" -e --emm | Enable sending EMM's to CAMD. Default: %s\n", ts->emm_send ? "enabled" : "disabled");
  207. printf(" -E --emm-only | Send only EMMs to CAMD, skipping ECMs and without\n");
  208. printf(" . decoding the input stream.\n");
  209. printf(" -Z --emm-pid <pid> | Force EMM pid. Default: none\n");
  210. printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
  211. printf(" . received/processed. Set <sec> to 0 to disable\n");
  212. printf(" . the reports. Default: %d sec\n", ts->emm_report_interval);
  213. printf("\n");
  214. printf("ECM options:\n");
  215. printf(" -X --ecm-pid <pid> | Force ECM pid. Default: none\n");
  216. printf(" -H --ecm-report-time <sec> | Report each <sec> how much ECMs and CWs have been\n");
  217. printf(" . processed/skipped. Set <sec> to 0 to disable\n");
  218. printf(" . the reports. Default: %d sec\n", ts->ecm_report_interval);
  219. printf(" -G --ecm-irdeto-type <int> | Process IRDETO ECMs with type X /0-3/. Default: %d\n", ts->irdeto_ecm);
  220. printf(" -K --ecm-no-log | Disable ECM and code words logging.\n");
  221. printf(" -J --cw-warn-time <sec> | Warn if no valid code word has been received.\n");
  222. printf(" . Set <sec> to 0 to disable. Default: %d sec\n", ts->cw_warn_sec);
  223. printf("\n");
  224. printf("Logging options:\n");
  225. printf(" -S --syslog | Log messages using syslog.\n");
  226. printf(" -l --syslog-host <host> | Syslog server address. Default: disabled\n");
  227. printf(" -L --syslog-port <port> | Syslog server port. Default: %d\n", ts->syslog_port);
  228. printf(" -F --log-file <filename> | Log to file <filename>.\n");
  229. printf(" -D --debug <level> | Message debug level.\n");
  230. printf(" . 0 = default messages\n");
  231. printf(" . 1 = show PSI tables\n");
  232. printf(" . 2 = show EMMs\n");
  233. printf(" . 3 = show duplicate ECMs\n");
  234. printf(" . 4 = packet debug\n");
  235. printf(" . 5 = packet debug + packet dump\n");
  236. printf("\n");
  237. printf("Misc options:\n");
  238. printf(" -j --pid-report | Report how much packets were received.\n");
  239. printf(" -b --bench | Benchmark decrypton.\n");
  240. printf(" -h --help | Show help screen.\n");
  241. printf(" -V --version | Show program version.\n");
  242. printf("\n");
  243. }
  244. static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_mode) {
  245. io->type = WTF_IO;
  246. char *p = strrchr(opt, ':');
  247. if (!p) {
  248. io->type = FILE_IO;
  249. if (strcmp(opt, "-") != 0) {
  250. io->fd = open(opt, open_flags, open_mode);
  251. if (io->fd < 0) {
  252. fprintf(stderr, "ERROR: Can not open file (%s): %s\n", opt, strerror(errno));
  253. exit(EXIT_FAILURE);
  254. }
  255. }
  256. io->fname = strdup(opt);
  257. return 0;
  258. }
  259. *p = 0x00;
  260. io->type = NET_IO;
  261. io->port = atoi(p + 1);
  262. if (inet_aton(opt, &io->addr) == 0)
  263. return 1;
  264. return 0;
  265. }
  266. static void parse_options(struct ts *ts, int argc, char **argv) {
  267. int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0, port_set = 0;
  268. while ((j = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
  269. char *p = NULL;
  270. switch (j) {
  271. case 'i': // -- ident
  272. ts->ident = optarg;
  273. break;
  274. case 'd': // --daemon
  275. ts->pidfile = optarg;
  276. break;
  277. case 'N': // --notify-program
  278. ts->notify_program = optarg;
  279. break;
  280. case 'S': // --syslog
  281. ts->syslog_active = 1;
  282. ts->syslog_remote = 0;
  283. break;
  284. case 'l': // --syslog-host
  285. ts->syslog_host = optarg;
  286. ts->syslog_active = 1;
  287. ts->syslog_remote = 1;
  288. break;
  289. case 'L': // --syslog-port
  290. ts->syslog_port = atoi(optarg);
  291. break;
  292. case 'F': // --log-file
  293. log_filename = optarg;
  294. break;
  295. case 'I': // --input
  296. input_addr_err = parse_io_param(&ts->input, optarg, O_RDONLY, 0);
  297. break;
  298. case 'R': // --input-rtp
  299. ts->rtp_input = !ts->rtp_input;
  300. break;
  301. case 'z': // --input-ignore-disc
  302. ts->ts_discont = !ts->ts_discont;
  303. break;
  304. case 'M': // --input-service
  305. ts->forced_service_id = strtoul(optarg, NULL, 0) & 0xffff;
  306. break;
  307. case 'T': // --input-buffer
  308. ts->input_buffer_time = strtoul(optarg, NULL, 0);
  309. break;
  310. case 'W': // --input-dump
  311. ts->input_dump_filename = optarg;
  312. break;
  313. case 'O': // --output
  314. output_addr_err = parse_io_param(&ts->output, optarg,
  315. O_CREAT | O_WRONLY | O_TRUNC,
  316. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  317. break;
  318. case 'o': // --output-intf
  319. if (inet_aton(optarg, &ts->output.intf) == 0)
  320. output_intf_err = 1;
  321. break;
  322. case 't': // --output-ttl
  323. ts->output.ttl = atoi(optarg);
  324. break;
  325. case 'r': // --output-rtp
  326. ts->rtp_output = 1;
  327. break;
  328. case 'k': // --output-rtp-ssrc
  329. ts->rtp_ssrc = strtoul(optarg, NULL, 0);
  330. break;
  331. case 'g': // --output-tos
  332. ts->output.tos = (uint8_t)strtol(optarg, NULL, 0);
  333. break;
  334. case 'p': // --no-output-filter
  335. ts->pid_filter = 0;
  336. break;
  337. case 'y': // --output-nit-pass
  338. ts->nit_passthrough = !ts->nit_passthrough;
  339. break;
  340. case 'w': // --output-eit-pass
  341. ts->eit_passthrough = !ts->eit_passthrough;
  342. break;
  343. case 'x': // --output-tdt-pass
  344. ts->tdt_passthrough = !ts->tdt_passthrough;
  345. break;
  346. case 'c': // --ca-system
  347. if (strcasecmp("IRDETO", optarg) == 0)
  348. ts->req_CA_sys = CA_IRDETO;
  349. else if (strcasecmp("CONNAX", optarg) == 0 || strcasecmp("CONAX", optarg) == 0)
  350. ts->req_CA_sys = CA_CONAX;
  351. else if (strcasecmp("CRYPTOWORKS", optarg) == 0)
  352. ts->req_CA_sys = CA_CRYPTOWORKS;
  353. else if (strcasecmp("SECA", optarg) == 0 || strcasecmp("MEDIAGUARD", optarg) == 0)
  354. ts->req_CA_sys = CA_SECA;
  355. else if (strcasecmp("VIACCESS", optarg) == 0)
  356. ts->req_CA_sys = CA_VIACCESS;
  357. else if (strcasecmp("VIDEOGUARD", optarg) == 0 || strcasecmp("NDS", optarg) == 0)
  358. ts->req_CA_sys = CA_VIDEOGUARD;
  359. else if (strcasecmp("NAGRA", optarg) == 0)
  360. ts->req_CA_sys = CA_NAGRA;
  361. else if (strcasecmp("DRE-CRYPT", optarg) == 0 || strcasecmp("DRECRYPT", optarg) == 0)
  362. ts->req_CA_sys = CA_DRECRYPT;
  363. else
  364. ca_err = 1;
  365. break;
  366. case 'C': // --caid
  367. ts->forced_caid = strtoul(optarg, NULL, 0) & 0xffff;
  368. break;
  369. case 'Y': // --const-cw
  370. ts->camd.constant_codeword = 1;
  371. if (strlen(optarg) != CODEWORD_LENGTH * 2) {
  372. fprintf(stderr, "ERROR: Constant code word should be %u characters long.\n", CODEWORD_LENGTH * 2);
  373. exit(EXIT_FAILURE);
  374. }
  375. if (decode_hex_string(optarg, ts->camd.key->cw, strlen(optarg)) < 0) {
  376. fprintf(stderr, "ERROR: Invalid hex string for constant code word: %s\n", optarg);
  377. exit(EXIT_FAILURE);
  378. }
  379. camd_set_cw(ts, ts->camd.key->cw, 0);
  380. ts->camd.key->is_valid_cw = 1;
  381. break;
  382. case 'A': // --camd-proto
  383. if (strcasecmp(optarg, "cs378x") == 0) {
  384. camd_proto_cs378x(&ts->camd.ops);
  385. } else if (strcasecmp(optarg, "newcamd") == 0) {
  386. camd_proto_newcamd(&ts->camd.ops);
  387. } else {
  388. fprintf(stderr, "Unknown CAMD protocol: %s\n", optarg);
  389. exit(EXIT_FAILURE);
  390. }
  391. break;
  392. case 's': // --camd-server
  393. p = strrchr(optarg, ':');
  394. if (p) {
  395. *p = 0x00;
  396. ts->camd.server_port = atoi(p + 1);
  397. port_set = 1;
  398. }
  399. if (inet_aton(optarg, &ts->camd.server_addr) == 0)
  400. server_err = 1;
  401. else
  402. server_err = 0;
  403. break;
  404. case 'U': // --camd-user
  405. if (strlen(optarg) < 64)
  406. ts->camd.user = optarg;
  407. break;
  408. case 'P': // --camd-pass
  409. ts->camd.pass = optarg;
  410. break;
  411. case 'B': // --camd-des-key
  412. if (strlen(optarg) != DESKEY_LENGTH) {
  413. fprintf(stderr, "ERROR: des key should be %u characters long.\n", DESKEY_LENGTH);
  414. exit(EXIT_FAILURE);
  415. }
  416. strncpy(ts->camd.newcamd.hex_des_key, optarg, sizeof(ts->camd.newcamd.hex_des_key) - 1);
  417. ts->camd.newcamd.hex_des_key[sizeof(ts->camd.newcamd.hex_des_key) - 1] = 0;
  418. break;
  419. case 'e': // --emm
  420. ts->emm_send = !ts->emm_send;
  421. break;
  422. case 'Z': // --emm-pid
  423. ts->forced_emm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  424. break;
  425. case 'E': // --emm-only
  426. ts->emm_only = 1;
  427. ts->emm_send = 1;
  428. break;
  429. case 'f': // --emm-report-time
  430. ts->emm_report_interval = strtoul(optarg, NULL, 10);
  431. if (ts->emm_report_interval > 86400)
  432. ts->emm_report_interval = 86400;
  433. break;
  434. case 'X': // --ecm-pid
  435. ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  436. break;
  437. case 'H': // --ecm-report-time
  438. ts->ecm_report_interval = strtoul(optarg, NULL, 10);
  439. if (ts->ecm_report_interval > 86400)
  440. ts->ecm_report_interval = 86400;
  441. break;
  442. case 'G': // --ecm-irdeto-type
  443. ts->irdeto_ecm = atoi(optarg);
  444. break;
  445. case 'K': // --ecm-no-log
  446. ts->ecm_cw_log = !ts->ecm_cw_log;
  447. break;
  448. case 'J': // --cw-warn-time
  449. ts->cw_warn_sec = strtoul(optarg, NULL, 10);
  450. if (ts->cw_warn_sec > 86400)
  451. ts->cw_warn_sec = 86400;
  452. ts->cw_last_warn= ts->cw_last_warn + ts->cw_warn_sec;
  453. break;
  454. case 'D': // --debug
  455. ts->debug_level = atoi(optarg);
  456. if (ts->debug_level > 0)
  457. ts->pid_report = 1;
  458. break;
  459. case 'j': // --pid-report
  460. ts->pid_report = 1;
  461. break;
  462. case 'b': // --bench
  463. run_benchmark();
  464. exit(EXIT_SUCCESS);
  465. case 'h': // --help
  466. show_help(ts);
  467. exit(EXIT_SUCCESS);
  468. case 'V': // --version
  469. printf("%s\n", program_id);
  470. exit(EXIT_SUCCESS);
  471. }
  472. }
  473. if (!ts->ident) {
  474. if (ts->syslog_active || ts->notify_program)
  475. ident_err = 1;
  476. }
  477. // Constant codeword is special. Disable conflicting options
  478. if (ts->camd.constant_codeword) {
  479. server_err = 0; // No server settings are required
  480. ts->forced_caid = 0;
  481. ts->forced_ecm_pid = 0;
  482. ts->forced_emm_pid = 0;
  483. ts->emm_send = 0;
  484. ts->emm_report_interval = 0;
  485. ts->ecm_report_interval = 0;
  486. ts->cw_warn_sec = 0;
  487. }
  488. if (ident_err || ca_err || server_err || input_addr_err || output_addr_err || ts->input.type == WTF_IO || ts->output.type == WTF_IO) {
  489. show_help(ts);
  490. if (ident_err)
  491. fprintf(stderr, "ERROR: Ident is not set, please use --ident option.\n");
  492. if (ca_err)
  493. fprintf(stderr, "ERROR: Requested CA system is unsupported.\n");
  494. if (server_err)
  495. fprintf(stderr, "ERROR: CAMD server IP address is not set or it is invalid.\n");
  496. if (input_addr_err)
  497. fprintf(stderr, "ERROR: Input IP address is invalid.\n");
  498. if (output_addr_err)
  499. fprintf(stderr, "ERROR: Output IP address is invalid.\n");
  500. if (output_intf_err)
  501. fprintf(stderr, "ERROR: Output interface address is invalid.\n");
  502. exit(EXIT_FAILURE);
  503. }
  504. if (decode_hex_string(ts->camd.newcamd.hex_des_key, ts->camd.newcamd.bin_des_key, DESKEY_LENGTH) < 0) {
  505. fprintf(stderr, "ERROR: Invalid hex string for des key: %s\n", ts->camd.newcamd.hex_des_key);
  506. exit(EXIT_FAILURE);
  507. }
  508. if (ts->camd.ops.proto == CAMD_NEWCAMD && !port_set) {
  509. fprintf(stderr, "ERROR: CAMD server port is not set. Use --camd-server %s:xxxx to set the port.\n", inet_ntoa(ts->camd.server_addr));
  510. exit(EXIT_FAILURE);
  511. }
  512. if (log_filename) {
  513. log_file = fopen(log_filename, "a");
  514. if (!log_file) {
  515. fprintf(stderr, "ERROR: Can't open log file %s: %s\n", log_filename, strerror(errno));
  516. exit(EXIT_FAILURE);
  517. }
  518. }
  519. if (ts->ident)
  520. ts_LOGf("Ident : %s\n", ts->ident);
  521. if (ts->notify_program)
  522. ts_LOGf("Notify prg : %s\n", ts->notify_program);
  523. if (ts->pidfile)
  524. ts_LOGf("Daemonize : %s pid file.\n", ts->pidfile);
  525. if (ts->syslog_active) {
  526. if (ts->syslog_remote)
  527. ts_LOGf("Syslog : %s:%d\n", ts->syslog_host, ts->syslog_port);
  528. else
  529. ts_LOGf("Syslog : enabled\n");
  530. } else
  531. ts_LOGf("Syslog : disabled\n");
  532. if (!ts->camd.constant_codeword) {
  533. if (ts->forced_caid)
  534. ts->req_CA_sys = ts_get_CA_sys(ts->forced_caid);
  535. if (!ts->forced_caid)
  536. ts_LOGf("CA System : %s\n", ts_get_CA_sys_txt(ts->req_CA_sys));
  537. else
  538. ts_LOGf("CA System : %s | CAID: 0x%04x (%d)\n",
  539. ts_get_CA_sys_txt(ts->req_CA_sys),
  540. ts->forced_caid, ts->forced_caid);
  541. } else {
  542. char cw_even[64], cw_odd[64];
  543. ts_hex_dump_buf(cw_even, sizeof(cw_even), ts->key.cw , 8, 0);
  544. ts_hex_dump_buf(cw_odd , sizeof(cw_odd ), ts->key.cw + 8, 8, 0);
  545. ts_LOGf("Constant CW: even = %s\n", cw_even);
  546. ts_LOGf("Constant CW: odd = %s\n", cw_odd);
  547. }
  548. if (ts->input.type == NET_IO) {
  549. ts_LOGf("Input addr : %s://%s:%u/\n",
  550. ts->rtp_input ? "rtp" : "udp",
  551. inet_ntoa(ts->input.addr), ts->input.port);
  552. if (ts->input_buffer_time) {
  553. ts_LOGf("Input buff : %u ms\n", ts->input_buffer_time);
  554. }
  555. } else if (ts->input.type == FILE_IO) {
  556. ts_LOGf("Input file : %s\n", ts->input.fd == 0 ? "STDIN" : ts->input.fname);
  557. }
  558. if (ts->input_dump_filename) {
  559. ts->input_dump_file = fopen(ts->input_dump_filename, "w");
  560. if (ts->input_dump_file)
  561. ts_LOGf("Input dump : %s\n", ts->input_dump_filename);
  562. else
  563. ts_LOGf("Input dump : %s | ERROR: %s\n", ts->input_dump_filename, strerror(errno));
  564. }
  565. if (ts->forced_service_id)
  566. ts_LOGf("Service id : 0x%04x (%d)\n",
  567. ts->forced_service_id, ts->forced_service_id);
  568. if (ts->req_CA_sys == CA_IRDETO)
  569. ts_LOGf("Irdeto ECM : %d\n", ts->irdeto_ecm);
  570. if (!ts->emm_only)
  571. {
  572. if (ts->output.type == NET_IO) {
  573. ts_LOGf("Output addr: %s://%s:%u/\n",
  574. ts->rtp_output ? "rtp" : "udp",
  575. inet_ntoa(ts->output.addr), ts->output.port);
  576. ts_LOGf("Output intf: %s\n", inet_ntoa(ts->output.intf));
  577. ts_LOGf("Output ttl : %d\n", ts->output.ttl);
  578. if (ts->output.tos > -1)
  579. ts_LOGf("Output TOS : %u (0x%02x)\n", ts->output.tos, ts->output.tos);
  580. if (ts->rtp_output) {
  581. ts_LOGf("RTP SSRC : %u (0x%04x)\n",
  582. ts->rtp_ssrc, ts->rtp_ssrc);
  583. // It is recommended that RTP seqnum starts with random number
  584. RAND_bytes((unsigned char *)&(ts->rtp_seqnum), 2);
  585. }
  586. } else if (ts->output.type == FILE_IO) {
  587. ts_LOGf("Output file: %s\n", ts->output.fd == 1 ? "STDOUT" : ts->output.fname);
  588. }
  589. ts_LOGf("Out filter : %s (%s)\n",
  590. ts->pid_filter ? "enabled" : "disabled",
  591. ts->pid_filter ? "output only service related PIDs" : "output everything"
  592. );
  593. if (ts->pid_filter) {
  594. if (ts->nit_passthrough)
  595. ts_LOGf("Out filter : Pass through NIT.\n");
  596. if (ts->eit_passthrough)
  597. ts_LOGf("Out filter : Pass through EIT (EPG).\n");
  598. if (ts->tdt_passthrough)
  599. ts_LOGf("Out filter : Pass through TDT/TOT.\n");
  600. }
  601. }
  602. if (!ts->camd.constant_codeword) {
  603. ts_LOGf("CAMD proto : %s\n", ts->camd.ops.ident);
  604. ts_LOGf("CAMD addr : tcp://%s:%u/\n", inet_ntoa(ts->camd.server_addr), ts->camd.server_port);
  605. ts_LOGf("CAMD user : %s\n", ts->camd.user);
  606. ts_LOGf("CAMD pass : %s\n", ts->camd.pass);
  607. if (ts->camd.ops.proto == CAMD_NEWCAMD)
  608. ts_LOGf("CAMD deskey: %s\n", ts->camd.newcamd.hex_des_key);
  609. }
  610. ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
  611. ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
  612. if (ts->emm_send && ts->emm_report_interval)
  613. ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
  614. if (ts->emm_send && ts->emm_report_interval == 0)
  615. ts_LOGf("EMM report : disabled\n");
  616. if (ts->forced_emm_pid)
  617. ts_LOGf("EMM pid : 0x%04x (%d)\n", ts->forced_emm_pid, ts->forced_emm_pid);
  618. if (ts->emm_only) {
  619. ts_LOGf("EMM only : %s\n", ts->emm_only ? "yes" : "no");
  620. } else {
  621. if (!ts->camd.constant_codeword)
  622. ts_LOGf("EMM send : %s\n", ts->emm_send ? "enabled" : "disabled");
  623. ts_LOGf("Decoding : %s\n", ts->threaded ? "threaded" : "single thread");
  624. }
  625. if (!ts->emm_only && ts->ecm_report_interval)
  626. ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);
  627. if (!ts->emm_only && ts->ecm_report_interval == 0 && !ts->camd.constant_codeword)
  628. ts_LOGf("ECM report : disabled\n");
  629. if (ts->forced_ecm_pid)
  630. ts_LOGf("ECM pid : 0x%04x (%d)\n", ts->forced_ecm_pid, ts->forced_ecm_pid);
  631. if (!ts->emm_only && ts->cw_warn_sec)
  632. ts_LOGf("CW warning : %d sec\n", ts->cw_warn_sec);
  633. if (!ts->emm_only && ts->cw_warn_sec && !ts->camd.constant_codeword)
  634. ts_LOGf("CW warning : disabled\n");
  635. if (!ts->ecm_cw_log && !ts->camd.constant_codeword)
  636. ts_LOGf("ECM/CW log : disabled\n");
  637. if (ts->ident) {
  638. int len = strlen(ts->ident);
  639. for (i = 0; i < len; i++) {
  640. if (ts->ident[i] == '/')
  641. ts->ident[i] = '-';
  642. }
  643. }
  644. }
  645. static void report_emms(struct ts *ts, time_t now) {
  646. ts_LOGf("EMM | Received %u and processed %u in %lu seconds.\n",
  647. ts->emm_seen_count,
  648. ts->emm_processed_count,
  649. now - ts->emm_last_report);
  650. if (ts->emm_seen_count == 0) {
  651. notify(ts, "NO_EMM_RECEIVED", "No EMMs were received in last %lu seconds.",
  652. now - ts->emm_last_report);
  653. }
  654. ts->emm_last_report = now;
  655. ts->emm_seen_count = 0;
  656. ts->emm_processed_count = 0;
  657. }
  658. static void report_ecms(struct ts *ts, time_t now) {
  659. ts_LOGf("ECM | Received %u (%u dup) and processed %u in %lu seconds.\n",
  660. ts->ecm_seen_count,
  661. ts->ecm_duplicate_count,
  662. ts->ecm_processed_count,
  663. now - ts->ecm_last_report);
  664. ts->ecm_last_report = now;
  665. ts->ecm_seen_count = 0;
  666. ts->ecm_duplicate_count = 0;
  667. ts->ecm_processed_count = 0;
  668. }
  669. static void report_cw_warn(struct ts *ts, time_t now) {
  670. if (now - ts->key.ts > 1) {
  671. notify(ts, "NO_CODE_WORD", "No valid code word was received in %ld sec.",
  672. now - ts->key.ts);
  673. ts_LOGf("CW | *ERR* No valid code word was received for %ld seconds!\n",
  674. now - ts->key.ts);
  675. }
  676. ts->cw_last_warn = now;
  677. ts->cw_next_warn = now + ts->cw_warn_sec;
  678. }
  679. static void do_reports(struct ts *ts) {
  680. static int first_emm_report = 1;
  681. static int first_ecm_report = 1;
  682. time_t now = time(NULL);
  683. if (ts->emm_send && ts->emm_report_interval) {
  684. if (first_emm_report && now >= ts->emm_last_report) {
  685. first_emm_report = 0;
  686. ts->emm_last_report -= FIRST_REPORT_SEC;
  687. report_emms(ts, now);
  688. } else if ((time_t)(ts->emm_last_report + ts->emm_report_interval) <= now) {
  689. report_emms(ts, now);
  690. }
  691. }
  692. if (!ts->emm_only && ts->ecm_report_interval) {
  693. if (first_ecm_report && now >= ts->ecm_last_report) {
  694. first_ecm_report = 0;
  695. ts->ecm_last_report -= FIRST_REPORT_SEC;
  696. report_ecms(ts, now);
  697. } else if ((time_t)(ts->ecm_last_report + ts->ecm_report_interval) <= now) {
  698. report_ecms(ts, now);
  699. }
  700. }
  701. if (!ts->emm_only && !ts->key.is_valid_cw) {
  702. if (ts->cw_warn_sec && now >= ts->cw_next_warn) {
  703. report_cw_warn(ts, now);
  704. }
  705. }
  706. }
  707. void signal_quit(int sig) {
  708. if (!keep_running)
  709. raise(sig);
  710. keep_running = 0;
  711. ts_LOGf("Killed %s with signal %d\n", program_id, sig);
  712. signal(sig, SIG_DFL);
  713. }
  714. #define RTP_HDR_SZ 12
  715. static uint8_t ts_packet[FRAME_SIZE + RTP_HDR_SZ];
  716. static uint8_t rtp_hdr[2][RTP_HDR_SZ];
  717. static struct ts ts;
  718. int main(int argc, char **argv) {
  719. ssize_t readen;
  720. int have_data = 1;
  721. int ntimeouts = 0;
  722. time_t timeout_start = time(NULL);
  723. int rtp_hdr_pos = 0, num_packets = 0;
  724. struct rlimit rl;
  725. if (getrlimit(RLIMIT_STACK, &rl) == 0) {
  726. if (rl.rlim_cur > THREAD_STACK_SIZE) {
  727. rl.rlim_cur = THREAD_STACK_SIZE;
  728. setrlimit(RLIMIT_STACK, &rl);
  729. }
  730. }
  731. memset(rtp_hdr[0], 0, RTP_HDR_SZ);
  732. memset(rtp_hdr[1], 0, RTP_HDR_SZ);
  733. data_init(&ts);
  734. ts_set_log_func(LOG_func);
  735. parse_options(&ts, argc, argv);
  736. if (ts.pidfile)
  737. daemonize(ts.pidfile);
  738. if (ts.syslog_active) {
  739. if (ts.syslog_remote) {
  740. log_init(ts.ident, 1, 1, ts.syslog_host, ts.syslog_port);
  741. remote_syslog = 1;
  742. } else {
  743. openlog(ts.ident, LOG_NDELAY | LOG_PID, LOG_USER);
  744. local_syslog = 1;
  745. }
  746. }
  747. ts.notify = notify_alloc(&ts);
  748. ts_LOGf("Start %s\n", program_id);
  749. notify(&ts, "START", "Starting %s", program_id);
  750. if (ts.input.type == NET_IO && udp_connect_input(&ts.input) < 1)
  751. goto EXIT;
  752. if (ts.output.type == NET_IO && udp_connect_output(&ts.output) < 1)
  753. goto EXIT;
  754. signal(SIGCHLD, SIG_IGN);
  755. signal(SIGPIPE, SIG_IGN);
  756. signal(SIGINT , signal_quit);
  757. signal(SIGTERM, signal_quit);
  758. if (ts.threaded) {
  759. pthread_create(&ts.decode_thread, &ts.thread_attr, &decode_thread, &ts);
  760. pthread_create(&ts.write_thread , &ts.thread_attr, &write_thread , &ts);
  761. }
  762. ts.emm_last_report = time(NULL) + FIRST_REPORT_SEC;
  763. ts.ecm_last_report = time(NULL) + FIRST_REPORT_SEC;
  764. camd_start(&ts);
  765. do {
  766. if (!ts.camd.constant_codeword)
  767. do_reports(&ts);
  768. if (ts.input.type == NET_IO) {
  769. set_log_io_errors(0);
  770. if (!ts.rtp_input) {
  771. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE, 250, 4, 1);
  772. } else {
  773. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE + RTP_HDR_SZ, 250, 4, 1);
  774. if (readen > RTP_HDR_SZ) {
  775. memcpy(rtp_hdr[rtp_hdr_pos], ts_packet, RTP_HDR_SZ);
  776. memmove(ts_packet, ts_packet + RTP_HDR_SZ, FRAME_SIZE);
  777. readen -= RTP_HDR_SZ;
  778. uint16_t ssrc = (rtp_hdr[rtp_hdr_pos][2] << 8) | rtp_hdr[rtp_hdr_pos][3];
  779. uint16_t pssrc = (rtp_hdr[!rtp_hdr_pos][2] << 8) | rtp_hdr[!rtp_hdr_pos][3];
  780. rtp_hdr_pos = !rtp_hdr_pos;
  781. if (pssrc + 1 != ssrc && (ssrc != 0 && pssrc != 0xffff) && num_packets > 2)
  782. if (ts.ts_discont)
  783. ts_LOGf("--- | RTP discontinuity last_ssrc %5d, curr_ssrc %5d, lost %d packet\n",
  784. pssrc, ssrc, ((ssrc - pssrc)-1) & 0xffff);
  785. num_packets++;
  786. }
  787. }
  788. set_log_io_errors(1);
  789. if (readen < 0) {
  790. ts_LOGf("--- | Input read timeout.\n");
  791. if (!ntimeouts) {
  792. timeout_start = time(NULL);
  793. notify(&ts, "INPUT_TIMEOUT", "Read timeout on input %s://%s:%u/",
  794. ts.rtp_input ? "rtp" : "udp",
  795. inet_ntoa(ts.input.addr), ts.input.port);
  796. }
  797. ntimeouts++;
  798. } else {
  799. if (ntimeouts && readen > 0) {
  800. notify(&ts, "INPUT_OK", "Data is available on input %s://%s:%u/ after %ld seconds timeout.",
  801. ts.rtp_input ? "rtp" : "udp",
  802. inet_ntoa(ts.input.addr), ts.input.port,
  803. (time(NULL) - timeout_start) + 2); // Timeout is detected when ~2 seconds there is no incoming data
  804. ntimeouts = 0;
  805. }
  806. }
  807. } else {
  808. readen = read(ts.input.fd, ts_packet, FRAME_SIZE);
  809. have_data = !(readen <= 0);
  810. }
  811. if (readen > 0) {
  812. if (ts.input_dump_file)
  813. fwrite(ts_packet, readen, 1, ts.input_dump_file);
  814. process_packets(&ts, ts_packet, readen);
  815. }
  816. if (!keep_running)
  817. break;
  818. } while (have_data);
  819. EXIT:
  820. camd_stop(&ts);
  821. if (ts.threaded) {
  822. ts.decode_stop = 1;
  823. ts.write_stop = 1;
  824. if (ts.decode_thread)
  825. pthread_join(ts.decode_thread, NULL);
  826. if (ts.write_thread)
  827. pthread_join(ts.write_thread, NULL);
  828. }
  829. show_pid_report(&ts);
  830. notify_sync(&ts, "STOP", "Stopping %s", program_id);
  831. ts_LOGf("Stop %s\n", program_id);
  832. if (ts.syslog_active) {
  833. if (ts.syslog_remote)
  834. log_close();
  835. else
  836. closelog();
  837. }
  838. if (ts.input_dump_file)
  839. fclose(ts.input_dump_file);
  840. if (ts.pidfile)
  841. unlink(ts.pidfile);
  842. if (log_file)
  843. fclose(log_file);
  844. notify_free(&ts.notify);
  845. data_free(&ts);
  846. exit(EXIT_SUCCESS);
  847. }